From f96694b18fdaa9bd6310debca71427068fe24046 Mon Sep 17 00:00:00 2001 From: Deon George Date: Mon, 22 Apr 2013 14:09:50 +1000 Subject: [PATCH] Kohana v3.3.0 --- .travis.yml | 24 + LICENSE.md | 14 + README.md | 19 + application/bootstrap.php | 129 + application/classes/Controller/Welcome.php | 10 + composer.json | 5 + composer.lock | 413 ++ composer.phar | Bin 0 -> 628852 bytes example.htaccess | 21 + index.php | 121 + install.php | 233 + modules/auth/README.md | 13 + modules/auth/classes/Auth.php | 3 + modules/auth/classes/Auth/File.php | 3 + modules/auth/classes/Kohana/Auth.php | 171 + modules/auth/classes/Kohana/Auth/File.php | 94 + modules/auth/config/auth.php | 17 + modules/auth/config/userguide.php | 23 + modules/auth/guide/auth/config.md | 13 + modules/auth/guide/auth/driver/develop.md | 79 + modules/auth/guide/auth/driver/file.md | 19 + modules/auth/guide/auth/index.md | 19 + modules/auth/guide/auth/login.md | 62 + modules/auth/guide/auth/menu.md | 6 + modules/cache/README.md | 59 + modules/cache/classes/Cache.php | 3 + modules/cache/classes/Cache/Apc.php | 3 + modules/cache/classes/Cache/Arithmetic.php | 3 + modules/cache/classes/Cache/Exception.php | 3 + modules/cache/classes/Cache/File.php | 3 + .../cache/classes/Cache/GarbageCollect.php | 3 + modules/cache/classes/Cache/Memcache.php | 3 + modules/cache/classes/Cache/MemcacheTag.php | 3 + modules/cache/classes/Cache/Sqlite.php | 3 + modules/cache/classes/Cache/Tagging.php | 3 + modules/cache/classes/Cache/Wincache.php | 3 + modules/cache/classes/HTTP/Cache.php | 3 + modules/cache/classes/Kohana/Cache.php | 300 ++ modules/cache/classes/Kohana/Cache/Apc.php | 166 + .../cache/classes/Kohana/Cache/Arithmetic.php | 39 + .../cache/classes/Kohana/Cache/Exception.php | 11 + modules/cache/classes/Kohana/Cache/File.php | 466 ++ .../classes/Kohana/Cache/GarbageCollect.php | 23 + .../cache/classes/Kohana/Cache/Memcache.php | 354 ++ .../classes/Kohana/Cache/MemcacheTag.php | 78 + modules/cache/classes/Kohana/Cache/Sqlite.php | 334 ++ .../cache/classes/Kohana/Cache/Tagging.php | 41 + .../cache/classes/Kohana/Cache/Wincache.php | 140 + modules/cache/classes/Kohana/HTTP/Cache.php | 503 ++ modules/cache/config/cache.php | 70 + modules/cache/config/userguide.php | 23 + modules/cache/guide/cache.usage.md | 219 + modules/cache/guide/cache/config.md | 162 + modules/cache/guide/cache/examples.md | 0 modules/cache/guide/cache/index.md | 57 + modules/cache/guide/cache/menu.md | 3 + modules/cache/guide/cache/usage.md | 219 + .../tests/cache/CacheBasicMethodsTest.php | 299 ++ modules/cache/tests/cache/CacheTest.php | 242 + modules/cache/tests/cache/FileTest.php | 98 + modules/cache/tests/cache/SqliteTest.php | 44 + modules/cache/tests/cache/WincacheTest.php | 39 + .../cache/tests/cache/arithmetic/ApcTest.php | 75 + .../arithmetic/CacheArithmeticMethods.php | 173 + .../tests/cache/arithmetic/MemcacheTest.php | 103 + .../tests/cache/request/client/CacheTest.php | 265 ++ modules/cache/tests/phpunit.xml | 19 + .../codebench/classes/Bench/ArrCallback.php | 57 + .../classes/Bench/AutoLinkEmails.php | 70 + modules/codebench/classes/Bench/DateSpan.php | 186 + .../codebench/classes/Bench/ExplodeLimit.php | 34 + modules/codebench/classes/Bench/GruberURL.php | 61 + .../codebench/classes/Bench/LtrimDigits.php | 28 + .../codebench/classes/Bench/MDDoBaseURL.php | 66 + .../codebench/classes/Bench/MDDoImageURL.php | 66 + .../classes/Bench/MDDoIncludeViews.php | 50 + .../classes/Bench/StripNullBytes.php | 37 + .../codebench/classes/Bench/Transliterate.php | 65 + modules/codebench/classes/Bench/URLSite.php | 123 + .../codebench/classes/Bench/UserFuncArray.php | 58 + .../codebench/classes/Bench/ValidColor.php | 116 + modules/codebench/classes/Bench/ValidURL.php | 105 + modules/codebench/classes/Codebench.php | 3 + .../classes/Controller/Codebench.php | 36 + .../codebench/classes/Kohana/Codebench.php | 217 + modules/codebench/config/codebench.php | 16 + modules/codebench/config/userguide.php | 23 + modules/codebench/guide/codebench/index.md | 76 + modules/codebench/guide/codebench/menu.md | 1 + modules/codebench/init.php | 8 + .../guide/codebench/codebench_screenshot1.png | Bin 0 -> 15827 bytes .../guide/codebench/codebench_screenshot2.png | Bin 0 -> 13575 bytes modules/codebench/views/codebench.php | 260 ++ modules/database/classes/Config/Database.php | 12 + .../classes/Config/Database/Reader.php | 15 + .../classes/Config/Database/Writer.php | 15 + modules/database/classes/DB.php | 3 + modules/database/classes/Database.php | 3 + .../database/classes/Database/Exception.php | 3 + .../database/classes/Database/Expression.php | 3 + modules/database/classes/Database/MySQL.php | 3 + .../classes/Database/MySQL/Result.php | 3 + modules/database/classes/Database/PDO.php | 3 + modules/database/classes/Database/Query.php | 3 + .../classes/Database/Query/Builder.php | 3 + .../classes/Database/Query/Builder/Delete.php | 3 + .../classes/Database/Query/Builder/Insert.php | 3 + .../classes/Database/Query/Builder/Join.php | 3 + .../classes/Database/Query/Builder/Select.php | 3 + .../classes/Database/Query/Builder/Update.php | 3 + .../classes/Database/Query/Builder/Where.php | 3 + modules/database/classes/Database/Result.php | 3 + .../classes/Database/Result/Cached.php | 3 + .../classes/Kohana/Config/Database.php | 15 + .../classes/Kohana/Config/Database/Reader.php | 66 + .../classes/Kohana/Config/Database/Writer.php | 110 + modules/database/classes/Kohana/DB.php | 139 + modules/database/classes/Kohana/Database.php | 726 +++ .../classes/Kohana/Database/Exception.php | 11 + .../classes/Kohana/Database/Expression.php | 138 + .../classes/Kohana/Database/MySQL.php | 443 ++ .../classes/Kohana/Database/MySQL/Result.php | 71 + .../database/classes/Kohana/Database/PDO.php | 247 + .../classes/Kohana/Database/Query.php | 262 ++ .../classes/Kohana/Database/Query/Builder.php | 248 + .../Kohana/Database/Query/Builder/Delete.php | 99 + .../Kohana/Database/Query/Builder/Insert.php | 181 + .../Kohana/Database/Query/Builder/Join.php | 149 + .../Kohana/Database/Query/Builder/Select.php | 446 ++ .../Kohana/Database/Query/Builder/Update.php | 140 + .../Kohana/Database/Query/Builder/Where.php | 180 + .../classes/Kohana/Database/Result.php | 338 ++ .../classes/Kohana/Database/Result/Cached.php | 51 + .../classes/Kohana/Model/Database.php | 63 + .../classes/Kohana/Session/Database.php | 239 + modules/database/classes/Model/Database.php | 3 + modules/database/classes/Session/Database.php | 3 + modules/database/config/database.php | 56 + modules/database/config/session.php | 27 + modules/database/config/userguide.php | 23 + modules/database/guide/database/config.md | 116 + modules/database/guide/database/examples.md | 52 + modules/database/guide/database/index.md | 17 + modules/database/guide/database/menu.md | 7 + modules/database/guide/database/query.md | 5 + .../database/guide/database/query/builder.md | 251 + .../guide/database/query/parameterized.md | 67 + modules/database/guide/database/results.md | 105 + modules/image/README.markdown | 0 modules/image/classes/Image.php | 3 + modules/image/classes/Image/GD.php | 3 + modules/image/classes/Image/Imagick.php | 3 + modules/image/classes/Kohana/Image.php | 761 +++ modules/image/classes/Kohana/Image/GD.php | 665 +++ .../image/classes/Kohana/Image/Imagick.php | 334 ++ modules/image/config/userguide.php | 23 + modules/image/guide/image/examples.md | 7 + modules/image/guide/image/examples/crop.md | 141 + modules/image/guide/image/examples/dynamic.md | 108 + modules/image/guide/image/examples/upload.md | 139 + modules/image/guide/image/index.md | 21 + modules/image/guide/image/menu.md | 6 + modules/image/guide/image/using.md | 112 + modules/image/media/guide/image/Thumbs.db | Bin 0 -> 19968 bytes modules/image/media/guide/image/crop_form.jpg | Bin 0 -> 15262 bytes modules/image/media/guide/image/crop_orig.jpg | Bin 0 -> 31360 bytes .../image/media/guide/image/crop_result.jpg | Bin 0 -> 15771 bytes .../image/media/guide/image/dynamic-400.jpg | Bin 0 -> 27609 bytes .../image/media/guide/image/dynamic-600.jpg | Bin 0 -> 45517 bytes .../image/media/guide/image/upload_form.jpg | Bin 0 -> 12478 bytes .../image/media/guide/image/upload_result.jpg | Bin 0 -> 18469 bytes modules/image/tests/kohana/ImageTest.php | 36 + modules/image/tests/test_data/test_image | Bin 0 -> 3455 bytes modules/minion/README.md | 62 + modules/minion/classes/Kohana/Minion/CLI.php | 315 ++ .../classes/Kohana/Minion/Exception.php | 64 + .../Kohana/Minion/Exception/InvalidTask.php | 18 + modules/minion/classes/Kohana/Minion/Task.php | 364 ++ modules/minion/classes/Minion/CLI.php | 3 + modules/minion/classes/Minion/Exception.php | 3 + .../classes/Minion/Exception/InvalidTask.php | 3 + modules/minion/classes/Minion/Task.php | 8 + modules/minion/classes/Task/Help.php | 28 + modules/minion/config/userguide.php | 13 + modules/minion/guide/minion/index.md | 3 + modules/minion/guide/minion/menu.md | 3 + modules/minion/guide/minion/setup.md | 32 + modules/minion/guide/minion/tasks.md | 71 + modules/minion/messages/validation.php | 5 + modules/minion/minion | 4 + modules/minion/miniond | 68 + modules/minion/tests/minion/task.php | 70 + .../minion/views/minion/error/validation.php | 10 + modules/minion/views/minion/help/error.php | 7 + modules/minion/views/minion/help/list.php | 17 + modules/minion/views/minion/help/task.php | 17 + modules/orm/auth-schema-mysql.sql | 49 + modules/orm/auth-schema-postgresql.sql | 53 + modules/orm/classes/Auth/ORM.php | 3 + modules/orm/classes/Kohana/Auth/ORM.php | 291 ++ modules/orm/classes/Kohana/ORM.php | 2354 ++++++++++ .../Kohana/ORM/Validation/Exception.php | 198 + modules/orm/classes/Model/Auth/Role.php | 31 + modules/orm/classes/Model/Auth/User.php | 204 + modules/orm/classes/Model/Auth/User/Token.php | 77 + modules/orm/classes/Model/Role.php | 7 + modules/orm/classes/Model/User.php | 7 + modules/orm/classes/Model/User/Token.php | 7 + modules/orm/classes/ORM.php | 3 + .../orm/classes/ORM/Validation/Exception.php | 10 + modules/orm/config/userguide.php | 23 + modules/orm/guide/orm/examples.md | 13 + modules/orm/guide/orm/examples/simple.md | 119 + modules/orm/guide/orm/examples/validation.md | 137 + modules/orm/guide/orm/filters.md | 41 + modules/orm/guide/orm/index.md | 22 + modules/orm/guide/orm/menu.md | 10 + modules/orm/guide/orm/models.md | 28 + modules/orm/guide/orm/relationships.md | 123 + modules/orm/guide/orm/tutorials.md | 0 modules/orm/guide/orm/upgrading.md | 16 + modules/orm/guide/orm/using.md | 94 + modules/orm/guide/orm/validation.md | 111 + modules/unittest/README.markdown | 24 + modules/unittest/bootstrap.php | 125 + modules/unittest/bootstrap_all_modules.php | 20 + .../Kohana/Unittest/Database/TestCase.php | 311 ++ .../classes/Kohana/Unittest/Helpers.php | 169 + .../classes/Kohana/Unittest/TestCase.php | 261 ++ .../classes/Kohana/Unittest/TestSuite.php | 80 + .../classes/Kohana/Unittest/Tests.php | 267 ++ .../classes/Unittest/Database/TestCase.php | 17 + modules/unittest/classes/Unittest/Helpers.php | 3 + .../unittest/classes/Unittest/TestCase.php | 3 + .../unittest/classes/Unittest/TestSuite.php | 3 + modules/unittest/classes/Unittest/Tests.php | 3 + modules/unittest/config/unittest.php | 34 + modules/unittest/config/userguide.php | 13 + modules/unittest/example.phpunit.xml | 16 + modules/unittest/guide/unittest/index.md | 3 + modules/unittest/guide/unittest/menu.md | 5 + .../unittest/guide/unittest/mockobjects.md | 265 ++ modules/unittest/guide/unittest/testing.md | 117 + .../guide/unittest/testing_workflows.md | 41 + .../guide/unittest/troubleshooting.md | 21 + modules/unittest/tests.php | 20 + modules/userguide/README.md | 69 + .../classes/Controller/Userguide.php | 3 + modules/userguide/classes/Kodoc.php | 3 + modules/userguide/classes/Kodoc/Class.php | 3 + modules/userguide/classes/Kodoc/Markdown.php | 3 + modules/userguide/classes/Kodoc/Method.php | 3 + .../userguide/classes/Kodoc/Method/Param.php | 3 + modules/userguide/classes/Kodoc/Missing.php | 3 + modules/userguide/classes/Kodoc/Property.php | 3 + .../classes/Kohana/Controller/Userguide.php | 398 ++ modules/userguide/classes/Kohana/Kodoc.php | 466 ++ .../userguide/classes/Kohana/Kodoc/Class.php | 279 ++ .../classes/Kohana/Kodoc/Markdown.php | 289 ++ .../userguide/classes/Kohana/Kodoc/Method.php | 141 + .../classes/Kohana/Kodoc/Method/Param.php | 101 + .../classes/Kohana/Kodoc/Missing.php | 36 + .../classes/Kohana/Kodoc/Property.php | 90 + modules/userguide/config/userguide.php | 39 + .../guide/de-de/about.conventions.md | 300 ++ modules/userguide/guide/de-de/about.kohana.md | 15 + modules/userguide/guide/developers.md | 150 + modules/userguide/guide/userguide/adding.md | 55 + modules/userguide/guide/userguide/config.md | 35 + .../userguide/guide/userguide/contributing.md | 75 + modules/userguide/guide/userguide/index.md | 3 + modules/userguide/guide/userguide/markdown.md | 237 + modules/userguide/guide/userguide/menu.md | 7 + modules/userguide/guide/userguide/modules.md | 0 modules/userguide/guide/userguide/using.md | 1 + modules/userguide/guide/userguide/works.md | 25 + modules/userguide/init.php | 44 + modules/userguide/media/guide/css/api.css | 34 + modules/userguide/media/guide/css/kodoc.css | 142 + modules/userguide/media/guide/css/print.css | 50 + modules/userguide/media/guide/css/screen.css | 267 ++ modules/userguide/media/guide/css/shCore.css | 226 + .../media/guide/css/shThemeDefault.css | 117 + .../media/guide/css/shThemeKodoc.css | 183 + modules/userguide/media/guide/img/arrows.png | Bin 0 -> 357 bytes .../userguide/media/guide/img/breadcrumbs.png | Bin 0 -> 195 bytes modules/userguide/media/guide/img/content.png | Bin 0 -> 2983 bytes .../userguide/media/guide/img/ext_link.png | Bin 0 -> 144 bytes modules/userguide/media/guide/img/h2_line.png | Bin 0 -> 184 bytes modules/userguide/media/guide/img/h3_line.png | Bin 0 -> 184 bytes modules/userguide/media/guide/img/header.png | Bin 0 -> 2998 bytes modules/userguide/media/guide/img/kohana.png | Bin 0 -> 8678 bytes .../media/guide/img/lightbulb_48.png | Bin 0 -> 4843 bytes modules/userguide/media/guide/img/lines.png | Bin 0 -> 181 bytes .../userguide/media/guide/img/orange-tab.png | Bin 0 -> 215 bytes modules/userguide/media/guide/img/wrapper.png | Bin 0 -> 12347 bytes .../userguide/media/guide/js/jquery.cookie.js | 96 + .../userguide/media/guide/js/jquery.min.js | 167 + modules/userguide/media/guide/js/kodoc.js | 97 + .../userguide/media/guide/js/shBrushPhp.js | 88 + modules/userguide/media/guide/js/shCore.js | 17 + modules/userguide/media/guide/js/sizzle.js | 1068 +++++ .../guide/userguide/contrib-github-edit.png | Bin 0 -> 9681 bytes .../guide/userguide/contrib-github-fork.png | Bin 0 -> 8278 bytes .../guide/userguide/contrib-github-pull.png | Bin 0 -> 8839 bytes modules/userguide/messages/userguide.php | 14 + modules/userguide/tests/KodocTest.php | 368 ++ .../tests/userguide/ControllerTest.php | 45 + .../userguide/vendor/markdown/License.text | 36 + .../userguide/vendor/markdown/markdown.php | 2909 ++++++++++++ .../userguide/views/userguide/api/class.php | 124 + .../userguide/views/userguide/api/menu.php | 18 + .../userguide/views/userguide/api/method.php | 45 + .../userguide/views/userguide/api/tags.php | 6 + modules/userguide/views/userguide/api/toc.php | 69 + modules/userguide/views/userguide/error.php | 3 + .../views/userguide/examples/error.php | 6 + .../userguide/examples/hello_world_error.php | 696 +++ modules/userguide/views/userguide/index.php | 20 + modules/userguide/views/userguide/menu.php | 17 + .../userguide/views/userguide/page-toc.php | 10 + .../userguide/views/userguide/template.php | 110 + system/classes/Arr.php | 3 + system/classes/Config.php | 3 + system/classes/Config/File.php | 3 + system/classes/Config/Group.php | 4 + system/classes/Controller.php | 3 + system/classes/Controller/Template.php | 3 + system/classes/Cookie.php | 3 + system/classes/Date.php | 3 + system/classes/Debug.php | 3 + system/classes/Encrypt.php | 3 + system/classes/Feed.php | 3 + system/classes/File.php | 3 + system/classes/Form.php | 3 + system/classes/Fragment.php | 3 + system/classes/HTML.php | 3 + system/classes/HTTP.php | 3 + system/classes/HTTP/Exception.php | 3 + system/classes/HTTP/Exception/300.php | 3 + system/classes/HTTP/Exception/301.php | 3 + system/classes/HTTP/Exception/302.php | 3 + system/classes/HTTP/Exception/303.php | 3 + system/classes/HTTP/Exception/304.php | 3 + system/classes/HTTP/Exception/305.php | 3 + system/classes/HTTP/Exception/307.php | 3 + system/classes/HTTP/Exception/400.php | 3 + system/classes/HTTP/Exception/401.php | 3 + system/classes/HTTP/Exception/402.php | 3 + system/classes/HTTP/Exception/403.php | 3 + system/classes/HTTP/Exception/404.php | 3 + system/classes/HTTP/Exception/405.php | 3 + system/classes/HTTP/Exception/406.php | 3 + system/classes/HTTP/Exception/407.php | 3 + system/classes/HTTP/Exception/408.php | 3 + system/classes/HTTP/Exception/409.php | 3 + system/classes/HTTP/Exception/410.php | 3 + system/classes/HTTP/Exception/411.php | 3 + system/classes/HTTP/Exception/412.php | 3 + system/classes/HTTP/Exception/413.php | 3 + system/classes/HTTP/Exception/414.php | 3 + system/classes/HTTP/Exception/415.php | 3 + system/classes/HTTP/Exception/416.php | 3 + system/classes/HTTP/Exception/417.php | 3 + system/classes/HTTP/Exception/500.php | 3 + system/classes/HTTP/Exception/501.php | 3 + system/classes/HTTP/Exception/502.php | 3 + system/classes/HTTP/Exception/503.php | 3 + system/classes/HTTP/Exception/504.php | 3 + system/classes/HTTP/Exception/505.php | 3 + system/classes/HTTP/Exception/Expected.php | 3 + system/classes/HTTP/Exception/Redirect.php | 3 + system/classes/HTTP/Header.php | 3 + system/classes/HTTP/Message.php | 3 + system/classes/HTTP/Request.php | 3 + system/classes/HTTP/Response.php | 3 + system/classes/I18n.php | 3 + system/classes/Inflector.php | 3 + system/classes/Kohana.php | 3 + system/classes/Kohana/Arr.php | 620 +++ system/classes/Kohana/Config.php | 192 + system/classes/Kohana/Config/File.php | 15 + system/classes/Kohana/Config/File/Reader.php | 56 + system/classes/Kohana/Config/Group.php | 130 + system/classes/Kohana/Config/Reader.php | 25 + system/classes/Kohana/Config/Source.php | 14 + system/classes/Kohana/Config/Writer.php | 27 + system/classes/Kohana/Controller.php | 145 + system/classes/Kohana/Controller/Template.php | 50 + system/classes/Kohana/Cookie.php | 161 + system/classes/Kohana/Core.php | 1048 +++++ system/classes/Kohana/Date.php | 603 +++ system/classes/Kohana/Debug.php | 465 ++ system/classes/Kohana/Encrypt.php | 213 + system/classes/Kohana/Exception.php | 3 + system/classes/Kohana/Feed.php | 185 + system/classes/Kohana/File.php | 241 + system/classes/Kohana/Form.php | 434 ++ system/classes/Kohana/Fragment.php | 147 + system/classes/Kohana/HTML.php | 345 ++ system/classes/Kohana/HTTP.php | 217 + system/classes/Kohana/HTTP/Exception.php | 72 + system/classes/Kohana/HTTP/Exception/300.php | 10 + system/classes/Kohana/HTTP/Exception/301.php | 10 + system/classes/Kohana/HTTP/Exception/302.php | 10 + system/classes/Kohana/HTTP/Exception/303.php | 10 + system/classes/Kohana/HTTP/Exception/304.php | 10 + system/classes/Kohana/HTTP/Exception/305.php | 41 + system/classes/Kohana/HTTP/Exception/307.php | 10 + system/classes/Kohana/HTTP/Exception/400.php | 10 + system/classes/Kohana/HTTP/Exception/401.php | 38 + system/classes/Kohana/HTTP/Exception/402.php | 10 + system/classes/Kohana/HTTP/Exception/403.php | 10 + system/classes/Kohana/HTTP/Exception/404.php | 10 + system/classes/Kohana/HTTP/Exception/405.php | 41 + system/classes/Kohana/HTTP/Exception/406.php | 10 + system/classes/Kohana/HTTP/Exception/407.php | 10 + system/classes/Kohana/HTTP/Exception/408.php | 10 + system/classes/Kohana/HTTP/Exception/409.php | 10 + system/classes/Kohana/HTTP/Exception/410.php | 10 + system/classes/Kohana/HTTP/Exception/411.php | 10 + system/classes/Kohana/HTTP/Exception/412.php | 10 + system/classes/Kohana/HTTP/Exception/413.php | 10 + system/classes/Kohana/HTTP/Exception/414.php | 10 + system/classes/Kohana/HTTP/Exception/415.php | 10 + system/classes/Kohana/HTTP/Exception/416.php | 10 + system/classes/Kohana/HTTP/Exception/417.php | 10 + system/classes/Kohana/HTTP/Exception/500.php | 10 + system/classes/Kohana/HTTP/Exception/501.php | 10 + system/classes/Kohana/HTTP/Exception/502.php | 10 + system/classes/Kohana/HTTP/Exception/503.php | 10 + system/classes/Kohana/HTTP/Exception/504.php | 10 + system/classes/Kohana/HTTP/Exception/505.php | 10 + .../Kohana/HTTP/Exception/Expected.php | 82 + .../Kohana/HTTP/Exception/Redirect.php | 51 + system/classes/Kohana/HTTP/Header.php | 949 ++++ system/classes/Kohana/HTTP/Message.php | 56 + system/classes/Kohana/HTTP/Request.php | 64 + system/classes/Kohana/HTTP/Response.php | 31 + system/classes/Kohana/I18n.php | 166 + system/classes/Kohana/Inflector.php | 269 ++ system/classes/Kohana/Kohana/Exception.php | 282 ++ system/classes/Kohana/Log.php | 228 + system/classes/Kohana/Log/File.php | 94 + system/classes/Kohana/Log/StdErr.php | 29 + system/classes/Kohana/Log/StdOut.php | 30 + system/classes/Kohana/Log/Syslog.php | 65 + system/classes/Kohana/Log/Writer.php | 95 + system/classes/Kohana/Model.php | 29 + system/classes/Kohana/Num.php | 234 + system/classes/Kohana/Profiler.php | 385 ++ system/classes/Kohana/Request.php | 1331 ++++++ system/classes/Kohana/Request/Client.php | 424 ++ system/classes/Kohana/Request/Client/Curl.php | 135 + .../Kohana/Request/Client/External.php | 207 + system/classes/Kohana/Request/Client/HTTP.php | 121 + .../Kohana/Request/Client/Internal.php | 128 + .../Request/Client/Recursion/Exception.php | 10 + .../classes/Kohana/Request/Client/Stream.php | 109 + system/classes/Kohana/Request/Exception.php | 9 + system/classes/Kohana/Response.php | 713 +++ system/classes/Kohana/Route.php | 629 +++ system/classes/Kohana/Security.php | 103 + system/classes/Kohana/Session.php | 505 ++ system/classes/Kohana/Session/Cookie.php | 55 + system/classes/Kohana/Session/Exception.php | 11 + system/classes/Kohana/Session/Native.php | 107 + system/classes/Kohana/Text.php | 686 +++ system/classes/Kohana/URL.php | 213 + system/classes/Kohana/UTF8.php | 767 +++ system/classes/Kohana/UTF8/Exception.php | 9 + system/classes/Kohana/Upload.php | 256 + system/classes/Kohana/Valid.php | 551 +++ system/classes/Kohana/Validation.php | 612 +++ .../classes/Kohana/Validation/Exception.php | 29 + system/classes/Kohana/View.php | 351 ++ system/classes/Kohana/View/Exception.php | 9 + system/classes/Log.php | 3 + system/classes/Log/File.php | 3 + system/classes/Log/StdErr.php | 3 + system/classes/Log/StdOut.php | 3 + system/classes/Log/Syslog.php | 3 + system/classes/Log/Writer.php | 3 + system/classes/Model.php | 3 + system/classes/Num.php | 3 + system/classes/Profiler.php | 3 + system/classes/Request.php | 3 + system/classes/Request/Client.php | 3 + system/classes/Request/Client/Curl.php | 3 + system/classes/Request/Client/External.php | 3 + system/classes/Request/Client/HTTP.php | 3 + system/classes/Request/Client/Internal.php | 3 + .../Request/Client/Recursion/Exception.php | 10 + system/classes/Request/Client/Stream.php | 3 + system/classes/Request/Exception.php | 9 + system/classes/Response.php | 3 + system/classes/Route.php | 3 + system/classes/Security.php | 3 + system/classes/Session.php | 3 + system/classes/Session/Cookie.php | 3 + system/classes/Session/Exception.php | 9 + system/classes/Session/Native.php | 3 + system/classes/Text.php | 3 + system/classes/URL.php | 3 + system/classes/UTF8.php | 3 + system/classes/UTF8/Exception.php | 9 + system/classes/Upload.php | 3 + system/classes/Valid.php | 3 + system/classes/Validation.php | 3 + system/classes/Validation/Exception.php | 3 + system/classes/View.php | 3 + system/classes/View/Exception.php | 9 + system/config/credit_cards.php | 60 + system/config/curl.php | 8 + system/config/encrypt.php | 17 + system/config/inflector.php | 98 + system/config/mimes.php | 227 + system/config/session.php | 7 + system/config/user_agents.php | 108 + system/config/userguide.php | 23 + system/guide/kohana/autoloading.md | 72 + system/guide/kohana/bootstrap.md | 111 + system/guide/kohana/config.md | 193 + system/guide/kohana/controllers.md | 1 + system/guide/kohana/conventions.md | 418 ++ system/guide/kohana/cookies.md | 100 + system/guide/kohana/debugging.md | 20 + system/guide/kohana/errors.md | 64 + system/guide/kohana/extension.md | 101 + system/guide/kohana/files.md | 83 + system/guide/kohana/files/classes.md | 41 + system/guide/kohana/files/config.md | 84 + system/guide/kohana/files/i18n.md | 67 + system/guide/kohana/files/messages.md | 36 + system/guide/kohana/flow.md | 27 + system/guide/kohana/fragments.md | 135 + system/guide/kohana/helpers.md | 53 + system/guide/kohana/index.md | 19 + system/guide/kohana/install.md | 45 + system/guide/kohana/menu.md | 49 + system/guide/kohana/modules.md | 40 + system/guide/kohana/mvc.md | 3 + system/guide/kohana/mvc/controllers.md | 181 + system/guide/kohana/mvc/models.md | 35 + system/guide/kohana/mvc/views.md | 153 + system/guide/kohana/profiling.md | 54 + system/guide/kohana/requests.md | 150 + system/guide/kohana/routing.md | 257 + system/guide/kohana/security.md | 1 + system/guide/kohana/security/cookies.md | 3 + system/guide/kohana/security/database.md | 5 + system/guide/kohana/security/deploying.md | 31 + system/guide/kohana/security/encryption.md | 107 + system/guide/kohana/security/validation.md | 266 ++ system/guide/kohana/security/xss.md | 17 + system/guide/kohana/sessions.md | 167 + system/guide/kohana/tips.md | 41 + system/guide/kohana/tutorials.md | 17 + system/guide/kohana/tutorials/clean-urls.md | 93 + system/guide/kohana/tutorials/error-pages.md | 99 + system/guide/kohana/tutorials/git.md | 143 + system/guide/kohana/tutorials/hello-world.md | 106 + .../guide/kohana/tutorials/library-kohana.md | 219 + .../guide/kohana/tutorials/sharing-kohana.md | 54 + system/guide/kohana/tutorials/simple-mvc.md | 1 + system/guide/kohana/tutorials/templates.md | 7 + system/guide/kohana/tutorials/translation.md | 5 + system/guide/kohana/upgrading.md | 100 + system/i18n/en.php | 3 + system/i18n/es.php | 7 + system/i18n/fr.php | 7 + .../guide/kohana/cascading_filesystem.png | Bin 0 -> 61164 bytes system/media/guide/kohana/hello_world_1.png | Bin 0 -> 1423 bytes system/media/guide/kohana/hello_world_2.png | Bin 0 -> 6681 bytes .../guide/kohana/hello_world_2_error.png | Bin 0 -> 84148 bytes system/media/guide/kohana/install.png | Bin 0 -> 71146 bytes system/media/guide/kohana/welcome.png | Bin 0 -> 754 bytes .../tests/validation/error_type_check.php | 7 + system/messages/validation.php | 27 + system/tests/kohana/ArrTest.php | 696 +++ .../tests/kohana/Config/File/ReaderTest.php | 94 + system/tests/kohana/Config/GroupTest.php | 192 + system/tests/kohana/ConfigTest.php | 406 ++ system/tests/kohana/CookieTest.php | 177 + system/tests/kohana/CoreTest.php | 369 ++ system/tests/kohana/DateTest.php | 790 ++++ system/tests/kohana/DebugTest.php | 126 + system/tests/kohana/ExceptionTest.php | 99 + system/tests/kohana/FeedTest.php | 123 + system/tests/kohana/FileTest.php | 81 + system/tests/kohana/FormTest.php | 408 ++ system/tests/kohana/HTMLTest.php | 367 ++ system/tests/kohana/HTTPTest.php | 87 + system/tests/kohana/Http/HeaderTest.php | 1491 ++++++ system/tests/kohana/I18nTest.php | 90 + system/tests/kohana/InflectorTest.php | 186 + system/tests/kohana/LogTest.php | 111 + system/tests/kohana/ModelTest.php | 36 + system/tests/kohana/NumTest.php | 205 + system/tests/kohana/RequestTest.php | 720 +++ system/tests/kohana/ResponseTest.php | 208 + system/tests/kohana/RouteTest.php | 840 ++++ system/tests/kohana/SecurityTest.php | 108 + system/tests/kohana/SessionTest.php | 502 ++ system/tests/kohana/TextTest.php | 642 +++ system/tests/kohana/URLTest.php | 279 ++ system/tests/kohana/UTF8Test.php | 631 +++ system/tests/kohana/UploadTest.php | 225 + system/tests/kohana/ValidTest.php | 985 ++++ system/tests/kohana/ValidationTest.php | 676 +++ system/tests/kohana/ViewTest.php | 83 + system/tests/kohana/request/ClientTest.php | 488 ++ .../kohana/request/client/ExternalTest.php | 191 + .../kohana/request/client/InternalTest.php | 68 + system/tests/test_data/callback_routes.php | 100 + system/tests/test_data/github.png | Bin 0 -> 5101 bytes system/tests/test_data/views/test.css.php | 1 + system/utf8/from_unicode.php | 72 + system/utf8/ltrim.php | 22 + system/utf8/ord.php | 72 + system/utf8/rtrim.php | 22 + system/utf8/str_ireplace.php | 70 + system/utf8/str_pad.php | 52 + system/utf8/str_split.php | 27 + system/utf8/strcasecmp.php | 19 + system/utf8/strcspn.php | 30 + system/utf8/stristr.php | 28 + system/utf8/strlen.php | 17 + system/utf8/strpos.php | 27 + system/utf8/strrev.php | 18 + system/utf8/strrpos.php | 27 + system/utf8/strspn.php | 30 + system/utf8/strtolower.php | 81 + system/utf8/strtoupper.php | 81 + system/utf8/substr.php | 72 + system/utf8/substr_replace.php | 22 + system/utf8/to_unicode.php | 145 + system/utf8/transliterate_to_ascii.php | 77 + system/utf8/trim.php | 17 + system/utf8/ucfirst.php | 18 + system/utf8/ucwords.php | 23 + system/views/kohana/error.php | 128 + system/views/kohana/generate_logo.php | 14 + system/views/kohana/logo.php | 8 + system/views/profiler/stats.php | 74 + system/views/profiler/style.css | 27 + vendor/autoload.php | 7 + vendor/bin/phpunit | 7 + vendor/composer/ClassLoader.php | 207 + vendor/composer/autoload_classmap.php | 351 ++ vendor/composer/autoload_namespaces.php | 10 + vendor/composer/autoload_real.php | 39 + vendor/composer/include_paths.php | 17 + vendor/composer/installed.json | 410 ++ .../phpunit/php-code-coverage/CONTRIBUTING.md | 5 + .../php-code-coverage/ChangeLog.markdown | 46 + vendor/phpunit/php-code-coverage/LICENSE | 33 + .../php-code-coverage/PHP/CodeCoverage.php | 796 ++++ .../PHP/CodeCoverage/Autoload.php | 90 + .../PHP/CodeCoverage/Autoload.php.in | 70 + .../PHP/CodeCoverage/Driver.php | 70 + .../PHP/CodeCoverage/Driver/Xdebug.php | 97 + .../PHP/CodeCoverage/Exception.php | 59 + .../PHP/CodeCoverage/Filter.php | 343 ++ .../PHP/CodeCoverage/Report/Clover.php | 346 ++ .../PHP/CodeCoverage/Report/Factory.php | 280 ++ .../PHP/CodeCoverage/Report/HTML.php | 221 + .../PHP/CodeCoverage/Report/HTML/Renderer.php | 269 ++ .../Report/HTML/Renderer/Dashboard.php | 256 + .../Report/HTML/Renderer/Directory.php | 132 + .../Report/HTML/Renderer/File.php | 583 +++ .../Renderer/Template/coverage_bar.html.dist | 3 + .../Template/css/bootstrap-responsive.min.css | 9 + .../Renderer/Template/css/bootstrap.min.css | 9 + .../HTML/Renderer/Template/css/style.css | 76 + .../Renderer/Template/dashboard.html.dist | 117 + .../Renderer/Template/directory.html.dist | 58 + .../Template/directory_item.html.dist | 13 + .../HTML/Renderer/Template/file.html.dist | 65 + .../Renderer/Template/file_item.html.dist | 14 + .../img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../Template/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes .../Renderer/Template/js/bootstrap.min.js | 6 + .../HTML/Renderer/Template/js/highcharts.js | 245 + .../HTML/Renderer/Template/js/jquery.min.js | 2 + .../Renderer/Template/method_item.html.dist | 11 + .../PHP/CodeCoverage/Report/Node.php | 380 ++ .../CodeCoverage/Report/Node/Directory.php | 512 ++ .../PHP/CodeCoverage/Report/Node/File.php | 721 +++ .../PHP/CodeCoverage/Report/Node/Iterator.php | 148 + .../PHP/CodeCoverage/Report/PHP.php | 74 + .../PHP/CodeCoverage/Report/Text.php | 278 ++ .../PHP/CodeCoverage/Util.php | 268 ++ .../Util/InvalidArgumentHelper.php | 80 + .../PHP/CodeCoverage/Version.php | 92 + .../phpunit/php-code-coverage/README.markdown | 43 + .../Tests/PHP/CodeCoverage/FilterTest.php | 298 ++ .../PHP/CodeCoverage/Report/CloverTest.php | 96 + .../PHP/CodeCoverage/Report/FactoryTest.php | 263 ++ .../Tests/PHP/CodeCoverage/UtilTest.php | 194 + .../Tests/PHP/CodeCoverageTest.php | 499 ++ .../php-code-coverage/Tests/TestCase.php | 266 ++ .../Tests/_files/BankAccount-clover.xml | 26 + .../Tests/_files/BankAccount.php | 33 + .../Tests/_files/BankAccountTest.php | 70 + .../_files/CoverageClassExtendedTest.php | 12 + .../Tests/_files/CoverageClassTest.php | 12 + .../Tests/_files/CoverageFunctionTest.php | 11 + .../CoverageMethodOneLineAnnotationTest.php | 12 + .../Tests/_files/CoverageMethodTest.php | 12 + .../Tests/_files/CoverageNoneTest.php | 9 + .../Tests/_files/CoverageNotPrivateTest.php | 12 + .../Tests/_files/CoverageNotProtectedTest.php | 12 + .../Tests/_files/CoverageNotPublicTest.php | 12 + .../Tests/_files/CoverageNothingTest.php | 13 + .../Tests/_files/CoveragePrivateTest.php | 12 + .../Tests/_files/CoverageProtectedTest.php | 12 + .../Tests/_files/CoveragePublicTest.php | 12 + .../CoverageTwoDefaultClassAnnotations.php | 19 + .../Tests/_files/CoveredClass.php | 36 + .../Tests/_files/CoveredFunction.php | 4 + .../NamespaceCoverageClassExtendedTest.php | 12 + .../_files/NamespaceCoverageClassTest.php | 12 + ...NamespaceCoverageCoversClassPublicTest.php | 16 + .../NamespaceCoverageCoversClassTest.php | 21 + .../_files/NamespaceCoverageMethodTest.php | 12 + .../NamespaceCoverageNotPrivateTest.php | 12 + .../NamespaceCoverageNotProtectedTest.php | 12 + .../_files/NamespaceCoverageNotPublicTest.php | 12 + .../_files/NamespaceCoveragePrivateTest.php | 12 + .../_files/NamespaceCoverageProtectedTest.php | 12 + .../_files/NamespaceCoveragePublicTest.php | 12 + .../Tests/_files/NamespaceCoveredClass.php | 38 + .../_files/NotExistingCoveredElementTest.php | 24 + .../Tests/_files/ignored-lines-clover.xml | 17 + .../Tests/_files/source_with_ignore.php | 38 + .../Tests/_files/source_with_namespace.php | 20 + .../source_with_oneline_annotations.php | 13 + .../Tests/_files/source_without_ignore.php | 4 + .../Tests/_files/source_without_namespace.php | 18 + vendor/phpunit/php-code-coverage/build.xml | 162 + .../ControlSignatureSniff.php | 22 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../php-code-coverage/build/PHPCS/ruleset.xml | 35 + .../phpunit/php-code-coverage/build/phpmd.xml | 27 + .../phpunit/php-code-coverage/composer.json | 41 + vendor/phpunit/php-code-coverage/package.xml | 133 + .../php-code-coverage/phpunit.xml.dist | 29 + .../php-code-coverage/scripts/auto_append.php | 5 + .../scripts/auto_prepend.php | 10 + .../php-file-iterator/ChangeLog.markdown | 26 + .../php-file-iterator/File/Iterator.php | 196 + .../File/Iterator/Autoload.php | 66 + .../File/Iterator/Autoload.php.in | 64 + .../File/Iterator/Facade.php | 161 + .../File/Iterator/Factory.php | 120 + vendor/phpunit/php-file-iterator/LICENSE | 33 + .../phpunit/php-file-iterator/README.markdown | 23 + vendor/phpunit/php-file-iterator/build.xml | 161 + .../ControlSignatureSniff.php | 22 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../php-file-iterator/build/PHPCS/ruleset.xml | 35 + .../phpunit/php-file-iterator/build/phpmd.xml | 27 + .../phpunit/php-file-iterator/composer.json | 33 + .../php-file-iterator/package-composer.json | 19 + vendor/phpunit/php-file-iterator/package.xml | 65 + .../php-text-template/ChangeLog.markdown | 19 + vendor/phpunit/php-text-template/LICENSE | 33 + .../phpunit/php-text-template/README.markdown | 23 + .../php-text-template/Text/Template.php | 153 + .../Text/Template/Autoload.php | 65 + .../Text/Template/Autoload.php.in | 65 + vendor/phpunit/php-text-template/build.xml | 161 + .../ControlSignatureSniff.php | 22 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../php-text-template/build/PHPCS/ruleset.xml | 35 + .../phpunit/php-text-template/build/phpmd.xml | 27 + .../phpunit/php-text-template/composer.json | 32 + .../php-text-template/package-composer.json | 19 + vendor/phpunit/php-text-template/package.xml | 59 + vendor/phpunit/php-timer/ChangeLog.markdown | 29 + vendor/phpunit/php-timer/LICENSE | 33 + vendor/phpunit/php-timer/PHP/Timer.php | 159 + .../phpunit/php-timer/PHP/Timer/Autoload.php | 66 + .../php-timer/PHP/Timer/Autoload.php.in | 66 + vendor/phpunit/php-timer/README.markdown | 23 + vendor/phpunit/php-timer/Tests/TimerTest.php | 108 + vendor/phpunit/php-timer/build.xml | 160 + .../ControlSignatureSniff.php | 22 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../phpunit/php-timer/build/PHPCS/ruleset.xml | 35 + vendor/phpunit/php-timer/build/phpmd.xml | 27 + vendor/phpunit/php-timer/composer.json | 32 + .../phpunit/php-timer/package-composer.json | 19 + vendor/phpunit/php-timer/package.xml | 59 + vendor/phpunit/php-timer/phpunit.xml.dist | 26 + .../php-token-stream/ChangeLog.markdown | 35 + vendor/phpunit/php-token-stream/LICENSE | 33 + vendor/phpunit/php-token-stream/PHP/Token.php | 717 +++ .../php-token-stream/PHP/Token/Stream.php | 568 +++ .../PHP/Token/Stream/Autoload.php | 226 + .../PHP/Token/Stream/Autoload.php.in | 65 + .../PHP/Token/Stream/CachingFactory.php | 85 + .../phpunit/php-token-stream/README.markdown | 23 + .../Tests/Token/ClassTest.php | 122 + .../Tests/Token/FunctionTest.php | 160 + .../Tests/Token/IncludeTest.php | 117 + .../Tests/Token/InterfaceTest.php | 236 + .../Tests/Token/NamespaceTest.php | 124 + .../php-token-stream/Tests/TokenTest.php | 85 + .../_files/classExtendsNamespacedClass.php | 10 + .../Tests/_files/classInNamespace.php | 6 + .../Tests/_files/classInScopedNamespace.php | 9 + .../php-token-stream/Tests/_files/issue19.php | 3 + ...tipleNamespacesWithOneClassUsingBraces.php | 12 + ...espacesWithOneClassUsingNonBraceSyntax.php | 14 + .../php-token-stream/Tests/_files/source.php | 32 + .../php-token-stream/Tests/_files/source2.php | 6 + .../php-token-stream/Tests/_files/source3.php | 14 + .../php-token-stream/Tests/_files/source4.php | 30 + vendor/phpunit/php-token-stream/build.xml | 162 + .../ControlSignatureSniff.php | 22 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../php-token-stream/build/PHPCS/ruleset.xml | 35 + .../phpunit/php-token-stream/build/phpmd.xml | 27 + vendor/phpunit/php-token-stream/composer.json | 33 + .../php-token-stream/package-composer.json | 19 + vendor/phpunit/php-token-stream/package.xml | 70 + .../phpunit/php-token-stream/phpunit.xml.dist | 27 + .../phpunit-mock-objects/CONTRIBUTING.md | 5 + .../phpunit-mock-objects/ChangeLog.markdown | 17 + vendor/phpunit/phpunit-mock-objects/LICENSE | 33 + .../PHPUnit/Framework/MockObject/Autoload.php | 100 + .../Framework/MockObject/Autoload.php.in | 65 + .../Framework/MockObject/Builder/Identity.php | 70 + .../MockObject/Builder/InvocationMocker.php | 193 + .../Framework/MockObject/Builder/Match.php | 66 + .../MockObject/Builder/MethodNameMatch.php | 68 + .../MockObject/Builder/Namespace.php | 79 + .../MockObject/Builder/ParametersMatch.php | 89 + .../Framework/MockObject/Builder/Stub.php | 66 + .../Framework/MockObject/Generator.php | 811 ++++ .../Generator/mocked_class.tpl.dist | 60 + .../Generator/mocked_clone.tpl.dist | 5 + .../Generator/mocked_object_method.tpl.dist | 22 + .../Generator/mocked_static_method.tpl.dist | 22 + .../MockObject/Generator/trait_class.tpl.dist | 4 + .../Generator/unmocked_clone.tpl.dist | 6 + .../MockObject/Generator/wsdl_class.tpl.dist | 9 + .../MockObject/Generator/wsdl_method.tpl.dist | 4 + .../Framework/MockObject/Invocation.php | 58 + .../MockObject/Invocation/Object.php | 75 + .../MockObject/Invocation/Static.php | 191 + .../Framework/MockObject/InvocationMocker.php | 201 + .../Framework/MockObject/Invokable.php | 79 + .../PHPUnit/Framework/MockObject/Matcher.php | 308 ++ .../MockObject/Matcher/AnyInvokedCount.php | 72 + .../MockObject/Matcher/AnyParameters.php | 74 + .../MockObject/Matcher/Invocation.php | 88 + .../MockObject/Matcher/InvokedAtIndex.php | 127 + .../MockObject/Matcher/InvokedAtLeastOnce.php | 85 + .../MockObject/Matcher/InvokedCount.php | 143 + .../MockObject/Matcher/InvokedRecorder.php | 107 + .../MockObject/Matcher/MethodName.php | 102 + .../MockObject/Matcher/Parameters.php | 160 + .../Matcher/StatelessInvocation.php | 96 + .../Framework/MockObject/MockBuilder.php | 291 ++ .../Framework/MockObject/MockObject.php | 94 + .../PHPUnit/Framework/MockObject/Stub.php | 71 + .../MockObject/Stub/ConsecutiveCalls.php | 87 + .../Framework/MockObject/Stub/Exception.php | 80 + .../MockObject/Stub/MatcherCollection.php | 66 + .../Framework/MockObject/Stub/Return.php | 78 + .../MockObject/Stub/ReturnArgument.php | 78 + .../MockObject/Stub/ReturnCallback.php | 94 + .../Framework/MockObject/Stub/ReturnSelf.php | 76 + .../MockObject/Stub/ReturnValueMap.php | 87 + .../Framework/MockObject/Verifiable.php | 65 + .../Tests/GeneratorTest.php | 89 + .../Tests/MockBuilderTest.php | 152 + .../MockObject/Invocation/ObjectTest.php | 82 + .../MockObject/Invocation/StaticTest.php | 52 + .../Tests/MockObject/class.phpt | 137 + .../MockObject/class_call_parent_clone.phpt | 91 + .../class_call_parent_constructor.phpt | 90 + .../class_dont_call_parent_clone.phpt | 90 + .../class_dont_call_parent_constructor.phpt | 90 + ...ing_interface_call_parent_constructor.phpt | 95 + ...nterface_dont_call_parent_constructor.phpt | 95 + .../Tests/MockObject/class_partial.phpt | 116 + .../Tests/MockObject/interface.phpt | 110 + .../invocation_object_clone_object.phpt | 139 + .../invocation_static_clone_object.phpt | 139 + .../Tests/MockObject/namespaced_class.phpt | 140 + .../namespaced_class_call_parent_clone.phpt | 93 + ...espaced_class_call_parent_constructor.phpt | 92 + ...mespaced_class_dont_call_parent_clone.phpt | 92 + ...ed_class_dont_call_parent_constructor.phpt | 92 + ...ing_interface_call_parent_constructor.phpt | 97 + ...nterface_dont_call_parent_constructor.phpt | 97 + .../MockObject/namespaced_class_partial.phpt | 118 + .../MockObject/namespaced_interface.phpt | 112 + .../Tests/MockObject/nonexistent_class.phpt | 87 + .../nonexistent_class_with_namespace.phpt | 95 + ...ith_namespace_starting_with_separator.phpt | 95 + .../Tests/MockObject/wsdl_class.phpt | 36 + .../MockObject/wsdl_class_namespace.phpt | 38 + .../Tests/MockObject/wsdl_class_partial.phpt | 29 + .../Tests/MockObjectTest.php | 552 +++ .../Tests/_files/AbstractMockTestClass.php | 5 + .../Tests/_files/AnInterface.php | 5 + .../Tests/_files/FunctionCallback.php | 9 + .../Tests/_files/GoogleSearch.wsdl | 198 + .../Tests/_files/MethodCallback.php | 21 + .../Tests/_files/Mockable.php | 28 + .../Tests/_files/PartialMockTestClass.php | 18 + .../Tests/_files/SomeClass.php | 13 + .../Tests/_files/StaticMockTestClass.php | 12 + vendor/phpunit/phpunit-mock-objects/build.xml | 162 + .../ControlSignatureSniff.php | 22 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../build/PHPCS/ruleset.xml | 35 + .../phpunit-mock-objects/build/phpmd.xml | 27 + .../phpunit-mock-objects/composer.json | 41 + .../package-composer.json | 19 + .../phpunit/phpunit-mock-objects/package.xml | 199 + .../phpunit-mock-objects/phpunit.xml.dist | 31 + vendor/phpunit/phpunit/CONTRIBUTING.md | 55 + vendor/phpunit/phpunit/ChangeLog.md | 82 + vendor/phpunit/phpunit/LICENSE | 33 + vendor/phpunit/phpunit/PHPUnit/Autoload.php | 208 + .../phpunit/phpunit/PHPUnit/Autoload.php.in | 91 + .../PHPUnit/Extensions/GroupTestSuite.php | 99 + .../PHPUnit/Extensions/PhptTestCase.php | 269 ++ .../Extensions/PhptTestCase/Logger.php | 62 + .../PHPUnit/Extensions/PhptTestSuite.php | 82 + .../PHPUnit/Extensions/RepeatedTest.php | 155 + .../PHPUnit/Extensions/TestDecorator.php | 149 + .../PHPUnit/Extensions/TicketListener.php | 224 + .../phpunit/PHPUnit/Framework/Assert.php | 2832 +++++++++++ .../PHPUnit/Framework/Assert/Functions.php | 1972 ++++++++ .../PHPUnit/Framework/Assert/Functions.php.in | 44 + .../Framework/AssertionFailedError.php | 68 + .../phpunit/PHPUnit/Framework/Comparator.php | 97 + .../PHPUnit/Framework/Comparator/Array.php | 177 + .../Framework/Comparator/DOMDocument.php | 114 + .../PHPUnit/Framework/Comparator/Double.php | 101 + .../Framework/Comparator/Exception.php | 92 + .../Framework/Comparator/MockObject.php | 86 + .../PHPUnit/Framework/Comparator/Numeric.php | 116 + .../PHPUnit/Framework/Comparator/Object.php | 145 + .../PHPUnit/Framework/Comparator/Resource.php | 97 + .../PHPUnit/Framework/Comparator/Scalar.php | 136 + .../Framework/Comparator/SplObjectStorage.php | 114 + .../PHPUnit/Framework/Comparator/Type.php | 105 + .../PHPUnit/Framework/ComparatorFactory.php | 156 + .../PHPUnit/Framework/ComparisonFailure.php | 166 + .../phpunit/PHPUnit/Framework/Constraint.php | 180 + .../PHPUnit/Framework/Constraint/And.php | 164 + .../Framework/Constraint/ArrayHasKey.php | 114 + .../Framework/Constraint/Attribute.php | 129 + .../PHPUnit/Framework/Constraint/Callback.php | 116 + .../Constraint/ClassHasAttribute.php | 124 + .../Constraint/ClassHasStaticAttribute.php | 98 + .../Framework/Constraint/Composite.php | 114 + .../PHPUnit/Framework/Constraint/Count.php | 128 + .../Framework/Constraint/Exception.php | 129 + .../Framework/Constraint/ExceptionCode.php | 109 + .../Framework/Constraint/ExceptionMessage.php | 109 + .../Framework/Constraint/FileExists.php | 102 + .../Framework/Constraint/GreaterThan.php | 96 + .../Framework/Constraint/IsAnything.php | 102 + .../PHPUnit/Framework/Constraint/IsEmpty.php | 104 + .../PHPUnit/Framework/Constraint/IsEqual.php | 215 + .../PHPUnit/Framework/Constraint/IsFalse.php | 82 + .../Framework/Constraint/IsIdentical.php | 172 + .../Framework/Constraint/IsInstanceOf.php | 121 + .../PHPUnit/Framework/Constraint/IsNull.php | 82 + .../PHPUnit/Framework/Constraint/IsTrue.php | 82 + .../PHPUnit/Framework/Constraint/IsType.php | 190 + .../Framework/Constraint/JsonMatches.php | 126 + .../JsonMatches/ErrorMessageProvider.php | 106 + .../PHPUnit/Framework/Constraint/LessThan.php | 96 + .../PHPUnit/Framework/Constraint/Not.php | 203 + .../Constraint/ObjectHasAttribute.php | 77 + .../PHPUnit/Framework/Constraint/Or.php | 157 + .../Framework/Constraint/PCREMatch.php | 105 + .../PHPUnit/Framework/Constraint/SameSize.php | 73 + .../Framework/Constraint/StringContains.php | 122 + .../Framework/Constraint/StringEndsWith.php | 96 + .../Framework/Constraint/StringMatches.php | 134 + .../Framework/Constraint/StringStartsWith.php | 96 + .../Constraint/TraversableContains.php | 152 + .../Constraint/TraversableContainsOnly.php | 135 + .../PHPUnit/Framework/Constraint/Xor.php | 162 + .../phpunit/PHPUnit/Framework/Error.php | 75 + .../PHPUnit/Framework/Error/Deprecated.php | 65 + .../PHPUnit/Framework/Error/Notice.php | 65 + .../PHPUnit/Framework/Error/Warning.php | 65 + .../phpunit/PHPUnit/Framework/Exception.php | 59 + .../Framework/ExpectationFailedException.php | 82 + .../PHPUnit/Framework/IncompleteTest.php | 60 + .../PHPUnit/Framework/IncompleteTestError.php | 60 + .../phpunit/PHPUnit/Framework/OutputError.php | 60 + .../Framework/Process/TestCaseMethod.tpl.dist | 51 + .../PHPUnit/Framework/SelfDescribing.php | 65 + .../phpunit/PHPUnit/Framework/SkippedTest.php | 59 + .../PHPUnit/Framework/SkippedTestError.php | 60 + .../Framework/SkippedTestSuiteError.php | 60 + .../PHPUnit/Framework/SyntheticError.php | 121 + .../phpunit/PHPUnit/Framework/Test.php | 66 + .../phpunit/PHPUnit/Framework/TestCase.php | 1880 ++++++++ .../phpunit/PHPUnit/Framework/TestFailure.php | 179 + .../PHPUnit/Framework/TestListener.php | 126 + .../phpunit/PHPUnit/Framework/TestResult.php | 956 ++++ .../phpunit/PHPUnit/Framework/TestSuite.php | 950 ++++ .../Framework/TestSuite/DataProvider.php | 70 + .../phpunit/PHPUnit/Framework/Warning.php | 125 + .../phpunit/PHPUnit/Runner/BaseTestRunner.php | 189 + .../Runner/StandardTestSuiteLoader.php | 153 + .../PHPUnit/Runner/TestSuiteLoader.php | 71 + .../phpunit/PHPUnit/Runner/Version.php | 100 + .../phpunit/PHPUnit/TextUI/Command.php | 897 ++++ .../phpunit/PHPUnit/TextUI/ResultPrinter.php | 664 +++ .../phpunit/PHPUnit/TextUI/TestRunner.php | 814 ++++ vendor/phpunit/phpunit/PHPUnit/Util/Class.php | 363 ++ .../phpunit/PHPUnit/Util/Configuration.php | 1026 ++++ .../PHPUnit/Util/DeprecatedFeature.php | 102 + .../PHPUnit/Util/DeprecatedFeature/Logger.php | 201 + vendor/phpunit/phpunit/PHPUnit/Util/Diff.php | 292 ++ .../phpunit/PHPUnit/Util/ErrorHandler.php | 132 + .../phpunit/PHPUnit/Util/Fileloader.php | 107 + .../phpunit/PHPUnit/Util/Filesystem.php | 81 + .../phpunit/phpunit/PHPUnit/Util/Filter.php | 146 + .../phpunit/phpunit/PHPUnit/Util/Getopt.php | 207 + .../phpunit/PHPUnit/Util/GlobalState.php | 427 ++ .../PHPUnit/Util/InvalidArgumentHelper.php | 80 + .../phpunit/phpunit/PHPUnit/Util/Log/JSON.php | 254 + .../phpunit/PHPUnit/Util/Log/JUnit.php | 482 ++ .../phpunit/phpunit/PHPUnit/Util/Log/TAP.php | 255 + vendor/phpunit/phpunit/PHPUnit/Util/PHP.php | 332 ++ .../phpunit/PHPUnit/Util/PHP/Default.php | 67 + .../phpunit/PHPUnit/Util/PHP/Windows.php | 90 + .../phpunit/phpunit/PHPUnit/Util/Printer.php | 208 + .../phpunit/phpunit/PHPUnit/Util/String.php | 118 + vendor/phpunit/phpunit/PHPUnit/Util/Test.php | 601 +++ .../PHPUnit/Util/TestDox/NamePrettifier.php | 177 + .../PHPUnit/Util/TestDox/ResultPrinter.php | 347 ++ .../Util/TestDox/ResultPrinter/HTML.php | 123 + .../Util/TestDox/ResultPrinter/Text.php | 95 + .../PHPUnit/Util/TestSuiteIterator.php | 148 + vendor/phpunit/phpunit/PHPUnit/Util/Type.php | 303 ++ vendor/phpunit/phpunit/PHPUnit/Util/XML.php | 916 ++++ vendor/phpunit/phpunit/README.md | 71 + .../Tests/Extensions/RepeatedTestTest.php | 108 + .../phpunit/Tests/Framework/AssertTest.php | 4158 +++++++++++++++++ .../Tests/Framework/ComparatorTest.php | 159 + .../JsonMatches/ErrorMessageProviderTest.php | 122 + .../Framework/Constraint/JsonMatchesTest.php | 90 + .../Tests/Framework/ConstraintTest.php | 3554 ++++++++++++++ .../phpunit/Tests/Framework/SuiteTest.php | 185 + .../phpunit/Tests/Framework/TestCaseTest.php | 439 ++ .../Tests/Framework/TestImplementorTest.php | 79 + .../Tests/Framework/TestListenerTest.php | 145 + .../phpunit/Tests/Regression/1021.phpt | 19 + .../Tests/Regression/1021/Issue1021Test.php | 23 + .../phpunit/phpunit/Tests/Regression/523.phpt | 19 + .../Tests/Regression/523/Issue523Test.php | 13 + .../phpunit/phpunit/Tests/Regression/578.phpt | 37 + .../Tests/Regression/578/Issue578Test.php | 20 + .../phpunit/phpunit/Tests/Regression/684.phpt | 26 + .../Tests/Regression/684/Issue684Test.php | 4 + .../phpunit/phpunit/Tests/Regression/783.phpt | 21 + .../Tests/Regression/783/ChildSuite.php | 15 + .../phpunit/Tests/Regression/783/OneTest.php | 10 + .../Tests/Regression/783/ParentSuite.php | 13 + .../phpunit/Tests/Regression/783/TwoTest.php | 10 + .../phpunit/Tests/Regression/GitHub/244.phpt | 40 + .../Regression/GitHub/244/Issue244Test.php | 55 + .../phpunit/Tests/Regression/GitHub/322.phpt | 28 + .../Regression/GitHub/322/Issue322Test.php | 17 + .../Regression/GitHub/322/phpunit322.xml | 11 + .../phpunit/Tests/Regression/GitHub/433.phpt | 33 + .../Regression/GitHub/433/Issue433Test.php | 21 + .../phpunit/Tests/Regression/GitHub/445.phpt | 34 + .../Regression/GitHub/445/Issue445Test.php | 21 + .../phpunit/Tests/Regression/GitHub/503.phpt | 34 + .../Regression/GitHub/503/Issue503Test.php | 11 + .../phpunit/Tests/Regression/GitHub/581.phpt | 43 + .../Regression/GitHub/581/Issue581Test.php | 10 + .../phpunit/Tests/Regression/GitHub/74.phpt | 31 + .../Regression/GitHub/74/Issue74Test.php | 9 + .../Regression/GitHub/74/NewException.php | 4 + .../Tests/Runner/BaseTestRunnerTest.php | 65 + .../Tests/TextUI/abstract-test-class.phpt | 29 + .../Tests/TextUI/concrete-test-class.phpt | 22 + .../dataprovider-log-xml-isolation.phpt | 49 + .../Tests/TextUI/dataprovider-log-xml.phpt | 50 + .../Tests/TextUI/dataprovider-testdox.phpt | 19 + .../phpunit/phpunit/Tests/TextUI/debug.phpt | 28 + .../Tests/TextUI/default-isolation.phpt | 22 + .../phpunit/phpunit/Tests/TextUI/default.phpt | 21 + .../Tests/TextUI/dependencies-isolation.phpt | 42 + .../phpunit/Tests/TextUI/dependencies.phpt | 42 + .../Tests/TextUI/dependencies2-isolation.phpt | 23 + .../phpunit/Tests/TextUI/dependencies2.phpt | 22 + .../Tests/TextUI/dependencies3-isolation.phpt | 23 + .../phpunit/Tests/TextUI/dependencies3.phpt | 21 + .../phpunit/Tests/TextUI/empty-testcase.phpt | 29 + .../phpunit/Tests/TextUI/exception-stack.phpt | 55 + .../Tests/TextUI/exclude-group-isolation.phpt | 24 + .../phpunit/Tests/TextUI/exclude-group.phpt | 23 + .../Tests/TextUI/failure-isolation.phpt | 144 + .../phpunit/phpunit/Tests/TextUI/failure.phpt | 144 + .../phpunit/Tests/TextUI/fatal-isolation.phpt | 35 + .../phpunit/phpunit/Tests/TextUI/fatal.phpt | 17 + .../Tests/TextUI/filter-class-isolation.phpt | 24 + .../phpunit/Tests/TextUI/filter-class.phpt | 23 + .../Tests/TextUI/filter-method-isolation.phpt | 24 + .../phpunit/Tests/TextUI/filter-method.phpt | 23 + .../Tests/TextUI/filter-no-results.phpt | 23 + .../phpunit/Tests/TextUI/group-isolation.phpt | 24 + .../phpunit/phpunit/Tests/TextUI/group.phpt | 23 + vendor/phpunit/phpunit/Tests/TextUI/help.phpt | 67 + .../phpunit/phpunit/Tests/TextUI/help2.phpt | 68 + .../phpunit/Tests/TextUI/list-groups.phpt | 22 + .../phpunit/Tests/TextUI/log-json.phpt | 77 + .../phpunit/phpunit/Tests/TextUI/log-tap.phpt | 28 + .../phpunit/phpunit/Tests/TextUI/log-xml.phpt | 31 + .../Tests/TextUI/strict-incomplete.phpt | 23 + .../Tests/TextUI/strict-isolation.phpt | 24 + .../phpunit/phpunit/Tests/TextUI/strict.phpt | 23 + vendor/phpunit/phpunit/Tests/TextUI/tap.phpt | 20 + .../Tests/TextUI/test-suffix-multiple.phpt | 22 + .../Tests/TextUI/test-suffix-single.phpt | 22 + .../phpunit/Tests/TextUI/testdox-html.phpt | 23 + .../phpunit/Tests/TextUI/testdox-text.phpt | 27 + .../phpunit/phpunit/Tests/TextUI/testdox.phpt | 21 + .../phpunit/phpunit/Tests/Util/ClassTest.php | 84 + .../phpunit/Tests/Util/ConfigurationTest.php | 390 ++ .../phpunit/phpunit/Tests/Util/DiffTest.php | 268 ++ .../Tests/Util/TestDox/NamePrettifierTest.php | 108 + .../phpunit/phpunit/Tests/Util/TestTest.php | 236 + .../phpunit/phpunit/Tests/Util/TypeTest.php | 269 ++ vendor/phpunit/phpunit/Tests/Util/XMLTest.php | 323 ++ .../phpunit/Tests/_files/AbstractTest.php | 7 + .../phpunit/phpunit/Tests/_files/Author.php | 66 + .../phpunit/Tests/_files/BankAccount.php | 116 + .../phpunit/Tests/_files/BankAccountTest.php | 133 + .../Tests/_files/BankAccountTest.test.php | 133 + vendor/phpunit/phpunit/Tests/_files/Book.php | 59 + .../phpunit/Tests/_files/Calculator.php | 14 + .../ChangeCurrentWorkingDirectoryTest.php | 10 + .../_files/ClassWithNonPublicAttributes.php | 29 + .../Tests/_files/ClassWithToString.php | 61 + .../phpunit/Tests/_files/ConcreteTest.my.php | 9 + .../phpunit/Tests/_files/ConcreteTest.php | 9 + .../phpunit/Tests/_files/DataProviderTest.php | 21 + .../Tests/_files/DependencyFailureTest.php | 22 + .../Tests/_files/DependencySuccessTest.php | 21 + .../Tests/_files/DependencyTestSuite.php | 16 + .../phpunit/Tests/_files/DoubleTestCase.php | 25 + .../Tests/_files/EmptyTestCaseTest.php | 4 + vendor/phpunit/phpunit/Tests/_files/Error.php | 8 + .../ExceptionInAssertPostConditionsTest.php | 35 + .../ExceptionInAssertPreConditionsTest.php | 35 + .../Tests/_files/ExceptionInSetUpTest.php | 35 + .../Tests/_files/ExceptionInTearDownTest.php | 35 + .../phpunit/Tests/_files/ExceptionInTest.php | 35 + .../Tests/_files/ExceptionNamespaceTest.php | 38 + .../phpunit/Tests/_files/ExceptionStack.php | 23 + .../phpunit/Tests/_files/ExceptionTest.php | 97 + .../phpunit/phpunit/Tests/_files/Failure.php | 8 + .../phpunit/Tests/_files/FailureTest.php | 76 + .../phpunit/Tests/_files/FatalTest.php | 10 + .../phpunit/Tests/_files/IncompleteTest.php | 8 + .../Tests/_files/InheritedTestCase.php | 9 + .../Tests/_files/JsonData/arrayObject.js | 1 + .../Tests/_files/JsonData/simpleObject.js | 1 + .../Tests/_files/JsonData/simpleObject2.js | 1 + .../phpunit/Tests/_files/MockRunner.php | 7 + .../Tests/_files/MultiDependencyTest.php | 23 + .../Tests/_files/NoArgTestCaseTest.php | 7 + .../phpunit/Tests/_files/NoTestCaseClass.php | 4 + .../phpunit/Tests/_files/NoTestCases.php | 7 + .../phpunit/Tests/_files/NonStatic.php | 8 + .../Tests/_files/NotPublicTestCase.php | 11 + .../phpunit/Tests/_files/NotVoidTestCase.php | 4 + .../phpunit/Tests/_files/NothingTest.php | 7 + .../phpunit/Tests/_files/OneTestCase.php | 11 + .../phpunit/Tests/_files/OutputTestCase.php | 27 + .../phpunit/Tests/_files/OverrideTestCase.php | 9 + .../_files/RequirementsClassDocBlockTest.php | 23 + .../phpunit/Tests/_files/RequirementsTest.php | 112 + .../phpunit/Tests/_files/SampleClass.php | 14 + .../_files/SelectorAssertionsFixture.html | 44 + .../phpunit/Tests/_files/Singleton.php | 22 + .../phpunit/Tests/_files/StackTest.php | 24 + .../phpunit/phpunit/Tests/_files/Struct.php | 10 + .../phpunit/phpunit/Tests/_files/Success.php | 7 + .../Tests/_files/TemplateMethodsTest.php | 52 + .../phpunit/Tests/_files/TestIterator.php | 36 + .../Tests/_files/ThrowExceptionTestCase.php | 8 + .../Tests/_files/ThrowNoExceptionTestCase.php | 7 + .../phpunit/phpunit/Tests/_files/WasRun.php | 10 + vendor/phpunit/phpunit/Tests/_files/bar.xml | 1 + .../phpunit/Tests/_files/configuration.xml | 115 + .../Tests/_files/configuration_xinclude.xml | 68 + .../Tests/_files/expectedFileFormat.txt | 1 + vendor/phpunit/phpunit/Tests/_files/foo.xml | 1 + ...uctureAttributesAreSameButValuesAreNot.xml | 10 + .../Tests/_files/structureExpected.xml | 10 + .../Tests/_files/structureIgnoreTextNodes.xml | 13 + .../_files/structureIsSameButDataIsNot.xml | 10 + .../structureWrongNumberOfAttributes.xml | 10 + .../_files/structureWrongNumberOfNodes.xml | 9 + vendor/phpunit/phpunit/build.xml | 220 + .../ControlSignatureSniff.php | 23 + .../Whitespace/ConcatenationSpacingSniff.php | 22 + .../phpunit/phpunit/build/PHPCS/ruleset.xml | 35 + vendor/phpunit/phpunit/build/assertions.php | 65 + .../build/dependencies/DbUnit-1.2.1.tgz | Bin 0 -> 41892 bytes .../dependencies/File_Iterator-1.3.3.tgz | Bin 0 -> 5152 bytes .../dependencies/PHPUnit_MockObject-1.2.1.tgz | Bin 0 -> 20417 bytes .../dependencies/PHPUnit_Selenium-1.2.9.tgz | Bin 0 -> 36429 bytes .../dependencies/PHP_CodeCoverage-1.2.6.tgz | Bin 0 -> 155960 bytes .../build/dependencies/PHP_Invoker-1.1.2.tgz | Bin 0 -> 3705 bytes .../build/dependencies/PHP_Timer-1.0.4.tgz | Bin 0 -> 3694 bytes .../dependencies/PHP_TokenStream-1.1.5.tgz | Bin 0 -> 9859 bytes .../dependencies/Text_Template-1.1.3.tgz | Bin 0 -> 3594 bytes .../phpunit/build/dependencies/Yaml-2.1.2.tgz | Bin 0 -> 38429 bytes .../phpunit/build/phar-autoload.php.in | 24 + vendor/phpunit/phpunit/build/phpmd.xml | 27 + vendor/phpunit/phpunit/composer.json | 62 + vendor/phpunit/phpunit/composer/bin/phpunit | 65 + vendor/phpunit/phpunit/package.xml | 292 ++ vendor/phpunit/phpunit/phpdox.xml.dist | 14 + vendor/phpunit/phpunit/phpunit.bat | 43 + vendor/phpunit/phpunit/phpunit.php | 46 + vendor/phpunit/phpunit/phpunit.xml.dist | 42 + vendor/phpunit/phpunit/phpunit.xsd | 251 + .../yaml/Symfony/Component/Yaml/CHANGELOG.md | 8 + .../yaml/Symfony/Component/Yaml/Dumper.php | 71 + .../yaml/Symfony/Component/Yaml/Escaper.php | 88 + .../Yaml/Exception/DumpException.php | 23 + .../Yaml/Exception/ExceptionInterface.php | 23 + .../Yaml/Exception/ParseException.php | 143 + .../yaml/Symfony/Component/Yaml/Inline.php | 424 ++ .../yaml/Symfony/Component/Yaml/LICENSE | 19 + .../yaml/Symfony/Component/Yaml/Parser.php | 620 +++ .../yaml/Symfony/Component/Yaml/README.md | 17 + .../Component/Yaml/Tests/DumperTest.php | 166 + .../Yaml/Tests/Fixtures/YtsAnchorAlias.yml | 31 + .../Yaml/Tests/Fixtures/YtsBasicTests.yml | 178 + .../Yaml/Tests/Fixtures/YtsBlockMapping.yml | 51 + .../Tests/Fixtures/YtsDocumentSeparator.yml | 85 + .../Yaml/Tests/Fixtures/YtsErrorTests.yml | 25 + .../Tests/Fixtures/YtsFlowCollections.yml | 60 + .../Yaml/Tests/Fixtures/YtsFoldedScalars.yml | 176 + .../Tests/Fixtures/YtsNullsAndEmpties.yml | 45 + .../Fixtures/YtsSpecificationExamples.yml | 1695 +++++++ .../Yaml/Tests/Fixtures/YtsTypeTransfers.yml | 244 + .../Yaml/Tests/Fixtures/embededPhp.yml | 1 + .../Yaml/Tests/Fixtures/escapedCharacters.yml | 139 + .../Component/Yaml/Tests/Fixtures/index.yml | 18 + .../Yaml/Tests/Fixtures/sfComments.yml | 51 + .../Yaml/Tests/Fixtures/sfCompact.yml | 159 + .../Yaml/Tests/Fixtures/sfMergeKey.yml | 27 + .../Yaml/Tests/Fixtures/sfObjects.yml | 11 + .../Yaml/Tests/Fixtures/sfQuotes.yml | 33 + .../Component/Yaml/Tests/Fixtures/sfTests.yml | 135 + .../Tests/Fixtures/unindentedCollections.yml | 62 + .../Component/Yaml/Tests/InlineTest.php | 211 + .../Component/Yaml/Tests/ParserTest.php | 193 + .../Symfony/Component/Yaml/Tests/YamlTest.php | 41 + .../Component/Yaml/Tests/bootstrap.php | 18 + .../yaml/Symfony/Component/Yaml/Unescaper.php | 145 + .../yaml/Symfony/Component/Yaml/Yaml.php | 113 + .../yaml/Symfony/Component/Yaml/composer.json | 31 + .../Symfony/Component/Yaml/phpunit.xml.dist | 29 + 1280 files changed, 145034 insertions(+) create mode 100644 .travis.yml create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 application/bootstrap.php create mode 100644 application/classes/Controller/Welcome.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 composer.phar create mode 100644 example.htaccess create mode 100644 index.php create mode 100644 install.php create mode 100644 modules/auth/README.md create mode 100644 modules/auth/classes/Auth.php create mode 100644 modules/auth/classes/Auth/File.php create mode 100644 modules/auth/classes/Kohana/Auth.php create mode 100644 modules/auth/classes/Kohana/Auth/File.php create mode 100644 modules/auth/config/auth.php create mode 100644 modules/auth/config/userguide.php create mode 100644 modules/auth/guide/auth/config.md create mode 100644 modules/auth/guide/auth/driver/develop.md create mode 100644 modules/auth/guide/auth/driver/file.md create mode 100644 modules/auth/guide/auth/index.md create mode 100644 modules/auth/guide/auth/login.md create mode 100644 modules/auth/guide/auth/menu.md create mode 100644 modules/cache/README.md create mode 100644 modules/cache/classes/Cache.php create mode 100644 modules/cache/classes/Cache/Apc.php create mode 100644 modules/cache/classes/Cache/Arithmetic.php create mode 100644 modules/cache/classes/Cache/Exception.php create mode 100644 modules/cache/classes/Cache/File.php create mode 100644 modules/cache/classes/Cache/GarbageCollect.php create mode 100644 modules/cache/classes/Cache/Memcache.php create mode 100644 modules/cache/classes/Cache/MemcacheTag.php create mode 100644 modules/cache/classes/Cache/Sqlite.php create mode 100644 modules/cache/classes/Cache/Tagging.php create mode 100644 modules/cache/classes/Cache/Wincache.php create mode 100644 modules/cache/classes/HTTP/Cache.php create mode 100644 modules/cache/classes/Kohana/Cache.php create mode 100644 modules/cache/classes/Kohana/Cache/Apc.php create mode 100644 modules/cache/classes/Kohana/Cache/Arithmetic.php create mode 100644 modules/cache/classes/Kohana/Cache/Exception.php create mode 100644 modules/cache/classes/Kohana/Cache/File.php create mode 100644 modules/cache/classes/Kohana/Cache/GarbageCollect.php create mode 100644 modules/cache/classes/Kohana/Cache/Memcache.php create mode 100644 modules/cache/classes/Kohana/Cache/MemcacheTag.php create mode 100644 modules/cache/classes/Kohana/Cache/Sqlite.php create mode 100644 modules/cache/classes/Kohana/Cache/Tagging.php create mode 100644 modules/cache/classes/Kohana/Cache/Wincache.php create mode 100644 modules/cache/classes/Kohana/HTTP/Cache.php create mode 100644 modules/cache/config/cache.php create mode 100644 modules/cache/config/userguide.php create mode 100644 modules/cache/guide/cache.usage.md create mode 100644 modules/cache/guide/cache/config.md create mode 100644 modules/cache/guide/cache/examples.md create mode 100644 modules/cache/guide/cache/index.md create mode 100644 modules/cache/guide/cache/menu.md create mode 100644 modules/cache/guide/cache/usage.md create mode 100644 modules/cache/tests/cache/CacheBasicMethodsTest.php create mode 100644 modules/cache/tests/cache/CacheTest.php create mode 100644 modules/cache/tests/cache/FileTest.php create mode 100644 modules/cache/tests/cache/SqliteTest.php create mode 100644 modules/cache/tests/cache/WincacheTest.php create mode 100644 modules/cache/tests/cache/arithmetic/ApcTest.php create mode 100644 modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php create mode 100644 modules/cache/tests/cache/arithmetic/MemcacheTest.php create mode 100644 modules/cache/tests/cache/request/client/CacheTest.php create mode 100644 modules/cache/tests/phpunit.xml create mode 100644 modules/codebench/classes/Bench/ArrCallback.php create mode 100644 modules/codebench/classes/Bench/AutoLinkEmails.php create mode 100644 modules/codebench/classes/Bench/DateSpan.php create mode 100644 modules/codebench/classes/Bench/ExplodeLimit.php create mode 100644 modules/codebench/classes/Bench/GruberURL.php create mode 100644 modules/codebench/classes/Bench/LtrimDigits.php create mode 100644 modules/codebench/classes/Bench/MDDoBaseURL.php create mode 100644 modules/codebench/classes/Bench/MDDoImageURL.php create mode 100644 modules/codebench/classes/Bench/MDDoIncludeViews.php create mode 100644 modules/codebench/classes/Bench/StripNullBytes.php create mode 100644 modules/codebench/classes/Bench/Transliterate.php create mode 100644 modules/codebench/classes/Bench/URLSite.php create mode 100644 modules/codebench/classes/Bench/UserFuncArray.php create mode 100644 modules/codebench/classes/Bench/ValidColor.php create mode 100644 modules/codebench/classes/Bench/ValidURL.php create mode 100644 modules/codebench/classes/Codebench.php create mode 100644 modules/codebench/classes/Controller/Codebench.php create mode 100644 modules/codebench/classes/Kohana/Codebench.php create mode 100644 modules/codebench/config/codebench.php create mode 100644 modules/codebench/config/userguide.php create mode 100644 modules/codebench/guide/codebench/index.md create mode 100644 modules/codebench/guide/codebench/menu.md create mode 100644 modules/codebench/init.php create mode 100644 modules/codebench/media/guide/codebench/codebench_screenshot1.png create mode 100644 modules/codebench/media/guide/codebench/codebench_screenshot2.png create mode 100644 modules/codebench/views/codebench.php create mode 100644 modules/database/classes/Config/Database.php create mode 100644 modules/database/classes/Config/Database/Reader.php create mode 100644 modules/database/classes/Config/Database/Writer.php create mode 100644 modules/database/classes/DB.php create mode 100644 modules/database/classes/Database.php create mode 100644 modules/database/classes/Database/Exception.php create mode 100644 modules/database/classes/Database/Expression.php create mode 100644 modules/database/classes/Database/MySQL.php create mode 100644 modules/database/classes/Database/MySQL/Result.php create mode 100644 modules/database/classes/Database/PDO.php create mode 100644 modules/database/classes/Database/Query.php create mode 100644 modules/database/classes/Database/Query/Builder.php create mode 100644 modules/database/classes/Database/Query/Builder/Delete.php create mode 100644 modules/database/classes/Database/Query/Builder/Insert.php create mode 100644 modules/database/classes/Database/Query/Builder/Join.php create mode 100644 modules/database/classes/Database/Query/Builder/Select.php create mode 100644 modules/database/classes/Database/Query/Builder/Update.php create mode 100644 modules/database/classes/Database/Query/Builder/Where.php create mode 100644 modules/database/classes/Database/Result.php create mode 100644 modules/database/classes/Database/Result/Cached.php create mode 100644 modules/database/classes/Kohana/Config/Database.php create mode 100644 modules/database/classes/Kohana/Config/Database/Reader.php create mode 100644 modules/database/classes/Kohana/Config/Database/Writer.php create mode 100644 modules/database/classes/Kohana/DB.php create mode 100644 modules/database/classes/Kohana/Database.php create mode 100644 modules/database/classes/Kohana/Database/Exception.php create mode 100644 modules/database/classes/Kohana/Database/Expression.php create mode 100644 modules/database/classes/Kohana/Database/MySQL.php create mode 100644 modules/database/classes/Kohana/Database/MySQL/Result.php create mode 100644 modules/database/classes/Kohana/Database/PDO.php create mode 100644 modules/database/classes/Kohana/Database/Query.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder/Delete.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder/Insert.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder/Join.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder/Select.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder/Update.php create mode 100644 modules/database/classes/Kohana/Database/Query/Builder/Where.php create mode 100644 modules/database/classes/Kohana/Database/Result.php create mode 100644 modules/database/classes/Kohana/Database/Result/Cached.php create mode 100644 modules/database/classes/Kohana/Model/Database.php create mode 100644 modules/database/classes/Kohana/Session/Database.php create mode 100644 modules/database/classes/Model/Database.php create mode 100644 modules/database/classes/Session/Database.php create mode 100644 modules/database/config/database.php create mode 100644 modules/database/config/session.php create mode 100644 modules/database/config/userguide.php create mode 100644 modules/database/guide/database/config.md create mode 100644 modules/database/guide/database/examples.md create mode 100644 modules/database/guide/database/index.md create mode 100644 modules/database/guide/database/menu.md create mode 100644 modules/database/guide/database/query.md create mode 100644 modules/database/guide/database/query/builder.md create mode 100644 modules/database/guide/database/query/parameterized.md create mode 100644 modules/database/guide/database/results.md create mode 100644 modules/image/README.markdown create mode 100644 modules/image/classes/Image.php create mode 100644 modules/image/classes/Image/GD.php create mode 100644 modules/image/classes/Image/Imagick.php create mode 100644 modules/image/classes/Kohana/Image.php create mode 100644 modules/image/classes/Kohana/Image/GD.php create mode 100644 modules/image/classes/Kohana/Image/Imagick.php create mode 100644 modules/image/config/userguide.php create mode 100644 modules/image/guide/image/examples.md create mode 100644 modules/image/guide/image/examples/crop.md create mode 100644 modules/image/guide/image/examples/dynamic.md create mode 100644 modules/image/guide/image/examples/upload.md create mode 100644 modules/image/guide/image/index.md create mode 100644 modules/image/guide/image/menu.md create mode 100644 modules/image/guide/image/using.md create mode 100644 modules/image/media/guide/image/Thumbs.db create mode 100644 modules/image/media/guide/image/crop_form.jpg create mode 100644 modules/image/media/guide/image/crop_orig.jpg create mode 100644 modules/image/media/guide/image/crop_result.jpg create mode 100644 modules/image/media/guide/image/dynamic-400.jpg create mode 100644 modules/image/media/guide/image/dynamic-600.jpg create mode 100644 modules/image/media/guide/image/upload_form.jpg create mode 100644 modules/image/media/guide/image/upload_result.jpg create mode 100644 modules/image/tests/kohana/ImageTest.php create mode 100644 modules/image/tests/test_data/test_image create mode 100644 modules/minion/README.md create mode 100644 modules/minion/classes/Kohana/Minion/CLI.php create mode 100644 modules/minion/classes/Kohana/Minion/Exception.php create mode 100644 modules/minion/classes/Kohana/Minion/Exception/InvalidTask.php create mode 100644 modules/minion/classes/Kohana/Minion/Task.php create mode 100644 modules/minion/classes/Minion/CLI.php create mode 100644 modules/minion/classes/Minion/Exception.php create mode 100644 modules/minion/classes/Minion/Exception/InvalidTask.php create mode 100644 modules/minion/classes/Minion/Task.php create mode 100644 modules/minion/classes/Task/Help.php create mode 100644 modules/minion/config/userguide.php create mode 100644 modules/minion/guide/minion/index.md create mode 100644 modules/minion/guide/minion/menu.md create mode 100644 modules/minion/guide/minion/setup.md create mode 100644 modules/minion/guide/minion/tasks.md create mode 100644 modules/minion/messages/validation.php create mode 100644 modules/minion/minion create mode 100644 modules/minion/miniond create mode 100644 modules/minion/tests/minion/task.php create mode 100644 modules/minion/views/minion/error/validation.php create mode 100644 modules/minion/views/minion/help/error.php create mode 100644 modules/minion/views/minion/help/list.php create mode 100644 modules/minion/views/minion/help/task.php create mode 100644 modules/orm/auth-schema-mysql.sql create mode 100644 modules/orm/auth-schema-postgresql.sql create mode 100644 modules/orm/classes/Auth/ORM.php create mode 100644 modules/orm/classes/Kohana/Auth/ORM.php create mode 100644 modules/orm/classes/Kohana/ORM.php create mode 100644 modules/orm/classes/Kohana/ORM/Validation/Exception.php create mode 100644 modules/orm/classes/Model/Auth/Role.php create mode 100644 modules/orm/classes/Model/Auth/User.php create mode 100644 modules/orm/classes/Model/Auth/User/Token.php create mode 100644 modules/orm/classes/Model/Role.php create mode 100644 modules/orm/classes/Model/User.php create mode 100644 modules/orm/classes/Model/User/Token.php create mode 100644 modules/orm/classes/ORM.php create mode 100644 modules/orm/classes/ORM/Validation/Exception.php create mode 100644 modules/orm/config/userguide.php create mode 100644 modules/orm/guide/orm/examples.md create mode 100644 modules/orm/guide/orm/examples/simple.md create mode 100644 modules/orm/guide/orm/examples/validation.md create mode 100644 modules/orm/guide/orm/filters.md create mode 100644 modules/orm/guide/orm/index.md create mode 100644 modules/orm/guide/orm/menu.md create mode 100644 modules/orm/guide/orm/models.md create mode 100644 modules/orm/guide/orm/relationships.md create mode 100644 modules/orm/guide/orm/tutorials.md create mode 100644 modules/orm/guide/orm/upgrading.md create mode 100644 modules/orm/guide/orm/using.md create mode 100644 modules/orm/guide/orm/validation.md create mode 100644 modules/unittest/README.markdown create mode 100644 modules/unittest/bootstrap.php create mode 100644 modules/unittest/bootstrap_all_modules.php create mode 100644 modules/unittest/classes/Kohana/Unittest/Database/TestCase.php create mode 100644 modules/unittest/classes/Kohana/Unittest/Helpers.php create mode 100644 modules/unittest/classes/Kohana/Unittest/TestCase.php create mode 100644 modules/unittest/classes/Kohana/Unittest/TestSuite.php create mode 100644 modules/unittest/classes/Kohana/Unittest/Tests.php create mode 100644 modules/unittest/classes/Unittest/Database/TestCase.php create mode 100644 modules/unittest/classes/Unittest/Helpers.php create mode 100644 modules/unittest/classes/Unittest/TestCase.php create mode 100644 modules/unittest/classes/Unittest/TestSuite.php create mode 100644 modules/unittest/classes/Unittest/Tests.php create mode 100644 modules/unittest/config/unittest.php create mode 100644 modules/unittest/config/userguide.php create mode 100644 modules/unittest/example.phpunit.xml create mode 100644 modules/unittest/guide/unittest/index.md create mode 100644 modules/unittest/guide/unittest/menu.md create mode 100644 modules/unittest/guide/unittest/mockobjects.md create mode 100644 modules/unittest/guide/unittest/testing.md create mode 100644 modules/unittest/guide/unittest/testing_workflows.md create mode 100644 modules/unittest/guide/unittest/troubleshooting.md create mode 100644 modules/unittest/tests.php create mode 100644 modules/userguide/README.md create mode 100644 modules/userguide/classes/Controller/Userguide.php create mode 100644 modules/userguide/classes/Kodoc.php create mode 100644 modules/userguide/classes/Kodoc/Class.php create mode 100644 modules/userguide/classes/Kodoc/Markdown.php create mode 100644 modules/userguide/classes/Kodoc/Method.php create mode 100644 modules/userguide/classes/Kodoc/Method/Param.php create mode 100644 modules/userguide/classes/Kodoc/Missing.php create mode 100644 modules/userguide/classes/Kodoc/Property.php create mode 100644 modules/userguide/classes/Kohana/Controller/Userguide.php create mode 100644 modules/userguide/classes/Kohana/Kodoc.php create mode 100644 modules/userguide/classes/Kohana/Kodoc/Class.php create mode 100644 modules/userguide/classes/Kohana/Kodoc/Markdown.php create mode 100644 modules/userguide/classes/Kohana/Kodoc/Method.php create mode 100644 modules/userguide/classes/Kohana/Kodoc/Method/Param.php create mode 100644 modules/userguide/classes/Kohana/Kodoc/Missing.php create mode 100644 modules/userguide/classes/Kohana/Kodoc/Property.php create mode 100644 modules/userguide/config/userguide.php create mode 100644 modules/userguide/guide/de-de/about.conventions.md create mode 100644 modules/userguide/guide/de-de/about.kohana.md create mode 100644 modules/userguide/guide/developers.md create mode 100644 modules/userguide/guide/userguide/adding.md create mode 100644 modules/userguide/guide/userguide/config.md create mode 100644 modules/userguide/guide/userguide/contributing.md create mode 100644 modules/userguide/guide/userguide/index.md create mode 100644 modules/userguide/guide/userguide/markdown.md create mode 100644 modules/userguide/guide/userguide/menu.md create mode 100644 modules/userguide/guide/userguide/modules.md create mode 100644 modules/userguide/guide/userguide/using.md create mode 100644 modules/userguide/guide/userguide/works.md create mode 100644 modules/userguide/init.php create mode 100644 modules/userguide/media/guide/css/api.css create mode 100644 modules/userguide/media/guide/css/kodoc.css create mode 100644 modules/userguide/media/guide/css/print.css create mode 100644 modules/userguide/media/guide/css/screen.css create mode 100644 modules/userguide/media/guide/css/shCore.css create mode 100644 modules/userguide/media/guide/css/shThemeDefault.css create mode 100644 modules/userguide/media/guide/css/shThemeKodoc.css create mode 100644 modules/userguide/media/guide/img/arrows.png create mode 100644 modules/userguide/media/guide/img/breadcrumbs.png create mode 100644 modules/userguide/media/guide/img/content.png create mode 100644 modules/userguide/media/guide/img/ext_link.png create mode 100644 modules/userguide/media/guide/img/h2_line.png create mode 100644 modules/userguide/media/guide/img/h3_line.png create mode 100644 modules/userguide/media/guide/img/header.png create mode 100644 modules/userguide/media/guide/img/kohana.png create mode 100644 modules/userguide/media/guide/img/lightbulb_48.png create mode 100644 modules/userguide/media/guide/img/lines.png create mode 100644 modules/userguide/media/guide/img/orange-tab.png create mode 100644 modules/userguide/media/guide/img/wrapper.png create mode 100644 modules/userguide/media/guide/js/jquery.cookie.js create mode 100644 modules/userguide/media/guide/js/jquery.min.js create mode 100644 modules/userguide/media/guide/js/kodoc.js create mode 100644 modules/userguide/media/guide/js/shBrushPhp.js create mode 100644 modules/userguide/media/guide/js/shCore.js create mode 100644 modules/userguide/media/guide/js/sizzle.js create mode 100644 modules/userguide/media/guide/userguide/contrib-github-edit.png create mode 100644 modules/userguide/media/guide/userguide/contrib-github-fork.png create mode 100644 modules/userguide/media/guide/userguide/contrib-github-pull.png create mode 100644 modules/userguide/messages/userguide.php create mode 100644 modules/userguide/tests/KodocTest.php create mode 100644 modules/userguide/tests/userguide/ControllerTest.php create mode 100644 modules/userguide/vendor/markdown/License.text create mode 100644 modules/userguide/vendor/markdown/markdown.php create mode 100644 modules/userguide/views/userguide/api/class.php create mode 100644 modules/userguide/views/userguide/api/menu.php create mode 100644 modules/userguide/views/userguide/api/method.php create mode 100644 modules/userguide/views/userguide/api/tags.php create mode 100644 modules/userguide/views/userguide/api/toc.php create mode 100644 modules/userguide/views/userguide/error.php create mode 100644 modules/userguide/views/userguide/examples/error.php create mode 100644 modules/userguide/views/userguide/examples/hello_world_error.php create mode 100644 modules/userguide/views/userguide/index.php create mode 100644 modules/userguide/views/userguide/menu.php create mode 100644 modules/userguide/views/userguide/page-toc.php create mode 100644 modules/userguide/views/userguide/template.php create mode 100644 system/classes/Arr.php create mode 100644 system/classes/Config.php create mode 100644 system/classes/Config/File.php create mode 100644 system/classes/Config/Group.php create mode 100644 system/classes/Controller.php create mode 100644 system/classes/Controller/Template.php create mode 100644 system/classes/Cookie.php create mode 100644 system/classes/Date.php create mode 100644 system/classes/Debug.php create mode 100644 system/classes/Encrypt.php create mode 100644 system/classes/Feed.php create mode 100644 system/classes/File.php create mode 100644 system/classes/Form.php create mode 100644 system/classes/Fragment.php create mode 100644 system/classes/HTML.php create mode 100644 system/classes/HTTP.php create mode 100644 system/classes/HTTP/Exception.php create mode 100644 system/classes/HTTP/Exception/300.php create mode 100644 system/classes/HTTP/Exception/301.php create mode 100644 system/classes/HTTP/Exception/302.php create mode 100644 system/classes/HTTP/Exception/303.php create mode 100644 system/classes/HTTP/Exception/304.php create mode 100644 system/classes/HTTP/Exception/305.php create mode 100644 system/classes/HTTP/Exception/307.php create mode 100644 system/classes/HTTP/Exception/400.php create mode 100644 system/classes/HTTP/Exception/401.php create mode 100644 system/classes/HTTP/Exception/402.php create mode 100644 system/classes/HTTP/Exception/403.php create mode 100644 system/classes/HTTP/Exception/404.php create mode 100644 system/classes/HTTP/Exception/405.php create mode 100644 system/classes/HTTP/Exception/406.php create mode 100644 system/classes/HTTP/Exception/407.php create mode 100644 system/classes/HTTP/Exception/408.php create mode 100644 system/classes/HTTP/Exception/409.php create mode 100644 system/classes/HTTP/Exception/410.php create mode 100644 system/classes/HTTP/Exception/411.php create mode 100644 system/classes/HTTP/Exception/412.php create mode 100644 system/classes/HTTP/Exception/413.php create mode 100644 system/classes/HTTP/Exception/414.php create mode 100644 system/classes/HTTP/Exception/415.php create mode 100644 system/classes/HTTP/Exception/416.php create mode 100644 system/classes/HTTP/Exception/417.php create mode 100644 system/classes/HTTP/Exception/500.php create mode 100644 system/classes/HTTP/Exception/501.php create mode 100644 system/classes/HTTP/Exception/502.php create mode 100644 system/classes/HTTP/Exception/503.php create mode 100644 system/classes/HTTP/Exception/504.php create mode 100644 system/classes/HTTP/Exception/505.php create mode 100644 system/classes/HTTP/Exception/Expected.php create mode 100644 system/classes/HTTP/Exception/Redirect.php create mode 100644 system/classes/HTTP/Header.php create mode 100644 system/classes/HTTP/Message.php create mode 100644 system/classes/HTTP/Request.php create mode 100644 system/classes/HTTP/Response.php create mode 100644 system/classes/I18n.php create mode 100644 system/classes/Inflector.php create mode 100644 system/classes/Kohana.php create mode 100644 system/classes/Kohana/Arr.php create mode 100644 system/classes/Kohana/Config.php create mode 100644 system/classes/Kohana/Config/File.php create mode 100644 system/classes/Kohana/Config/File/Reader.php create mode 100644 system/classes/Kohana/Config/Group.php create mode 100644 system/classes/Kohana/Config/Reader.php create mode 100644 system/classes/Kohana/Config/Source.php create mode 100644 system/classes/Kohana/Config/Writer.php create mode 100644 system/classes/Kohana/Controller.php create mode 100644 system/classes/Kohana/Controller/Template.php create mode 100644 system/classes/Kohana/Cookie.php create mode 100644 system/classes/Kohana/Core.php create mode 100644 system/classes/Kohana/Date.php create mode 100644 system/classes/Kohana/Debug.php create mode 100644 system/classes/Kohana/Encrypt.php create mode 100644 system/classes/Kohana/Exception.php create mode 100644 system/classes/Kohana/Feed.php create mode 100644 system/classes/Kohana/File.php create mode 100644 system/classes/Kohana/Form.php create mode 100644 system/classes/Kohana/Fragment.php create mode 100644 system/classes/Kohana/HTML.php create mode 100644 system/classes/Kohana/HTTP.php create mode 100644 system/classes/Kohana/HTTP/Exception.php create mode 100644 system/classes/Kohana/HTTP/Exception/300.php create mode 100644 system/classes/Kohana/HTTP/Exception/301.php create mode 100644 system/classes/Kohana/HTTP/Exception/302.php create mode 100644 system/classes/Kohana/HTTP/Exception/303.php create mode 100644 system/classes/Kohana/HTTP/Exception/304.php create mode 100644 system/classes/Kohana/HTTP/Exception/305.php create mode 100644 system/classes/Kohana/HTTP/Exception/307.php create mode 100644 system/classes/Kohana/HTTP/Exception/400.php create mode 100644 system/classes/Kohana/HTTP/Exception/401.php create mode 100644 system/classes/Kohana/HTTP/Exception/402.php create mode 100644 system/classes/Kohana/HTTP/Exception/403.php create mode 100644 system/classes/Kohana/HTTP/Exception/404.php create mode 100644 system/classes/Kohana/HTTP/Exception/405.php create mode 100644 system/classes/Kohana/HTTP/Exception/406.php create mode 100644 system/classes/Kohana/HTTP/Exception/407.php create mode 100644 system/classes/Kohana/HTTP/Exception/408.php create mode 100644 system/classes/Kohana/HTTP/Exception/409.php create mode 100644 system/classes/Kohana/HTTP/Exception/410.php create mode 100644 system/classes/Kohana/HTTP/Exception/411.php create mode 100644 system/classes/Kohana/HTTP/Exception/412.php create mode 100644 system/classes/Kohana/HTTP/Exception/413.php create mode 100644 system/classes/Kohana/HTTP/Exception/414.php create mode 100644 system/classes/Kohana/HTTP/Exception/415.php create mode 100644 system/classes/Kohana/HTTP/Exception/416.php create mode 100644 system/classes/Kohana/HTTP/Exception/417.php create mode 100644 system/classes/Kohana/HTTP/Exception/500.php create mode 100644 system/classes/Kohana/HTTP/Exception/501.php create mode 100644 system/classes/Kohana/HTTP/Exception/502.php create mode 100644 system/classes/Kohana/HTTP/Exception/503.php create mode 100644 system/classes/Kohana/HTTP/Exception/504.php create mode 100644 system/classes/Kohana/HTTP/Exception/505.php create mode 100644 system/classes/Kohana/HTTP/Exception/Expected.php create mode 100644 system/classes/Kohana/HTTP/Exception/Redirect.php create mode 100644 system/classes/Kohana/HTTP/Header.php create mode 100644 system/classes/Kohana/HTTP/Message.php create mode 100644 system/classes/Kohana/HTTP/Request.php create mode 100644 system/classes/Kohana/HTTP/Response.php create mode 100644 system/classes/Kohana/I18n.php create mode 100644 system/classes/Kohana/Inflector.php create mode 100644 system/classes/Kohana/Kohana/Exception.php create mode 100644 system/classes/Kohana/Log.php create mode 100644 system/classes/Kohana/Log/File.php create mode 100644 system/classes/Kohana/Log/StdErr.php create mode 100644 system/classes/Kohana/Log/StdOut.php create mode 100644 system/classes/Kohana/Log/Syslog.php create mode 100644 system/classes/Kohana/Log/Writer.php create mode 100644 system/classes/Kohana/Model.php create mode 100644 system/classes/Kohana/Num.php create mode 100644 system/classes/Kohana/Profiler.php create mode 100644 system/classes/Kohana/Request.php create mode 100644 system/classes/Kohana/Request/Client.php create mode 100644 system/classes/Kohana/Request/Client/Curl.php create mode 100644 system/classes/Kohana/Request/Client/External.php create mode 100644 system/classes/Kohana/Request/Client/HTTP.php create mode 100644 system/classes/Kohana/Request/Client/Internal.php create mode 100644 system/classes/Kohana/Request/Client/Recursion/Exception.php create mode 100644 system/classes/Kohana/Request/Client/Stream.php create mode 100644 system/classes/Kohana/Request/Exception.php create mode 100644 system/classes/Kohana/Response.php create mode 100644 system/classes/Kohana/Route.php create mode 100644 system/classes/Kohana/Security.php create mode 100644 system/classes/Kohana/Session.php create mode 100644 system/classes/Kohana/Session/Cookie.php create mode 100644 system/classes/Kohana/Session/Exception.php create mode 100644 system/classes/Kohana/Session/Native.php create mode 100644 system/classes/Kohana/Text.php create mode 100644 system/classes/Kohana/URL.php create mode 100644 system/classes/Kohana/UTF8.php create mode 100644 system/classes/Kohana/UTF8/Exception.php create mode 100644 system/classes/Kohana/Upload.php create mode 100644 system/classes/Kohana/Valid.php create mode 100644 system/classes/Kohana/Validation.php create mode 100644 system/classes/Kohana/Validation/Exception.php create mode 100644 system/classes/Kohana/View.php create mode 100644 system/classes/Kohana/View/Exception.php create mode 100644 system/classes/Log.php create mode 100644 system/classes/Log/File.php create mode 100644 system/classes/Log/StdErr.php create mode 100644 system/classes/Log/StdOut.php create mode 100644 system/classes/Log/Syslog.php create mode 100644 system/classes/Log/Writer.php create mode 100644 system/classes/Model.php create mode 100644 system/classes/Num.php create mode 100644 system/classes/Profiler.php create mode 100644 system/classes/Request.php create mode 100644 system/classes/Request/Client.php create mode 100644 system/classes/Request/Client/Curl.php create mode 100644 system/classes/Request/Client/External.php create mode 100644 system/classes/Request/Client/HTTP.php create mode 100644 system/classes/Request/Client/Internal.php create mode 100644 system/classes/Request/Client/Recursion/Exception.php create mode 100644 system/classes/Request/Client/Stream.php create mode 100644 system/classes/Request/Exception.php create mode 100644 system/classes/Response.php create mode 100644 system/classes/Route.php create mode 100644 system/classes/Security.php create mode 100644 system/classes/Session.php create mode 100644 system/classes/Session/Cookie.php create mode 100644 system/classes/Session/Exception.php create mode 100644 system/classes/Session/Native.php create mode 100644 system/classes/Text.php create mode 100644 system/classes/URL.php create mode 100644 system/classes/UTF8.php create mode 100644 system/classes/UTF8/Exception.php create mode 100644 system/classes/Upload.php create mode 100644 system/classes/Valid.php create mode 100644 system/classes/Validation.php create mode 100644 system/classes/Validation/Exception.php create mode 100644 system/classes/View.php create mode 100644 system/classes/View/Exception.php create mode 100644 system/config/credit_cards.php create mode 100644 system/config/curl.php create mode 100644 system/config/encrypt.php create mode 100644 system/config/inflector.php create mode 100644 system/config/mimes.php create mode 100644 system/config/session.php create mode 100644 system/config/user_agents.php create mode 100644 system/config/userguide.php create mode 100644 system/guide/kohana/autoloading.md create mode 100644 system/guide/kohana/bootstrap.md create mode 100644 system/guide/kohana/config.md create mode 100644 system/guide/kohana/controllers.md create mode 100644 system/guide/kohana/conventions.md create mode 100644 system/guide/kohana/cookies.md create mode 100644 system/guide/kohana/debugging.md create mode 100644 system/guide/kohana/errors.md create mode 100644 system/guide/kohana/extension.md create mode 100644 system/guide/kohana/files.md create mode 100644 system/guide/kohana/files/classes.md create mode 100644 system/guide/kohana/files/config.md create mode 100644 system/guide/kohana/files/i18n.md create mode 100644 system/guide/kohana/files/messages.md create mode 100644 system/guide/kohana/flow.md create mode 100644 system/guide/kohana/fragments.md create mode 100644 system/guide/kohana/helpers.md create mode 100644 system/guide/kohana/index.md create mode 100644 system/guide/kohana/install.md create mode 100644 system/guide/kohana/menu.md create mode 100644 system/guide/kohana/modules.md create mode 100644 system/guide/kohana/mvc.md create mode 100644 system/guide/kohana/mvc/controllers.md create mode 100644 system/guide/kohana/mvc/models.md create mode 100644 system/guide/kohana/mvc/views.md create mode 100644 system/guide/kohana/profiling.md create mode 100644 system/guide/kohana/requests.md create mode 100644 system/guide/kohana/routing.md create mode 100644 system/guide/kohana/security.md create mode 100644 system/guide/kohana/security/cookies.md create mode 100644 system/guide/kohana/security/database.md create mode 100644 system/guide/kohana/security/deploying.md create mode 100644 system/guide/kohana/security/encryption.md create mode 100644 system/guide/kohana/security/validation.md create mode 100644 system/guide/kohana/security/xss.md create mode 100644 system/guide/kohana/sessions.md create mode 100644 system/guide/kohana/tips.md create mode 100644 system/guide/kohana/tutorials.md create mode 100644 system/guide/kohana/tutorials/clean-urls.md create mode 100644 system/guide/kohana/tutorials/error-pages.md create mode 100644 system/guide/kohana/tutorials/git.md create mode 100644 system/guide/kohana/tutorials/hello-world.md create mode 100644 system/guide/kohana/tutorials/library-kohana.md create mode 100644 system/guide/kohana/tutorials/sharing-kohana.md create mode 100644 system/guide/kohana/tutorials/simple-mvc.md create mode 100644 system/guide/kohana/tutorials/templates.md create mode 100644 system/guide/kohana/tutorials/translation.md create mode 100644 system/guide/kohana/upgrading.md create mode 100644 system/i18n/en.php create mode 100644 system/i18n/es.php create mode 100644 system/i18n/fr.php create mode 100644 system/media/guide/kohana/cascading_filesystem.png create mode 100644 system/media/guide/kohana/hello_world_1.png create mode 100644 system/media/guide/kohana/hello_world_2.png create mode 100644 system/media/guide/kohana/hello_world_2_error.png create mode 100644 system/media/guide/kohana/install.png create mode 100644 system/media/guide/kohana/welcome.png create mode 100644 system/messages/tests/validation/error_type_check.php create mode 100644 system/messages/validation.php create mode 100644 system/tests/kohana/ArrTest.php create mode 100644 system/tests/kohana/Config/File/ReaderTest.php create mode 100644 system/tests/kohana/Config/GroupTest.php create mode 100644 system/tests/kohana/ConfigTest.php create mode 100644 system/tests/kohana/CookieTest.php create mode 100644 system/tests/kohana/CoreTest.php create mode 100644 system/tests/kohana/DateTest.php create mode 100644 system/tests/kohana/DebugTest.php create mode 100644 system/tests/kohana/ExceptionTest.php create mode 100644 system/tests/kohana/FeedTest.php create mode 100644 system/tests/kohana/FileTest.php create mode 100644 system/tests/kohana/FormTest.php create mode 100644 system/tests/kohana/HTMLTest.php create mode 100644 system/tests/kohana/HTTPTest.php create mode 100644 system/tests/kohana/Http/HeaderTest.php create mode 100644 system/tests/kohana/I18nTest.php create mode 100644 system/tests/kohana/InflectorTest.php create mode 100644 system/tests/kohana/LogTest.php create mode 100644 system/tests/kohana/ModelTest.php create mode 100644 system/tests/kohana/NumTest.php create mode 100644 system/tests/kohana/RequestTest.php create mode 100644 system/tests/kohana/ResponseTest.php create mode 100644 system/tests/kohana/RouteTest.php create mode 100644 system/tests/kohana/SecurityTest.php create mode 100644 system/tests/kohana/SessionTest.php create mode 100644 system/tests/kohana/TextTest.php create mode 100644 system/tests/kohana/URLTest.php create mode 100644 system/tests/kohana/UTF8Test.php create mode 100644 system/tests/kohana/UploadTest.php create mode 100644 system/tests/kohana/ValidTest.php create mode 100644 system/tests/kohana/ValidationTest.php create mode 100644 system/tests/kohana/ViewTest.php create mode 100644 system/tests/kohana/request/ClientTest.php create mode 100644 system/tests/kohana/request/client/ExternalTest.php create mode 100644 system/tests/kohana/request/client/InternalTest.php create mode 100644 system/tests/test_data/callback_routes.php create mode 100644 system/tests/test_data/github.png create mode 100644 system/tests/test_data/views/test.css.php create mode 100644 system/utf8/from_unicode.php create mode 100644 system/utf8/ltrim.php create mode 100644 system/utf8/ord.php create mode 100644 system/utf8/rtrim.php create mode 100644 system/utf8/str_ireplace.php create mode 100644 system/utf8/str_pad.php create mode 100644 system/utf8/str_split.php create mode 100644 system/utf8/strcasecmp.php create mode 100644 system/utf8/strcspn.php create mode 100644 system/utf8/stristr.php create mode 100644 system/utf8/strlen.php create mode 100644 system/utf8/strpos.php create mode 100644 system/utf8/strrev.php create mode 100644 system/utf8/strrpos.php create mode 100644 system/utf8/strspn.php create mode 100644 system/utf8/strtolower.php create mode 100644 system/utf8/strtoupper.php create mode 100644 system/utf8/substr.php create mode 100644 system/utf8/substr_replace.php create mode 100644 system/utf8/to_unicode.php create mode 100644 system/utf8/transliterate_to_ascii.php create mode 100644 system/utf8/trim.php create mode 100644 system/utf8/ucfirst.php create mode 100644 system/utf8/ucwords.php create mode 100644 system/views/kohana/error.php create mode 100644 system/views/kohana/generate_logo.php create mode 100644 system/views/kohana/logo.php create mode 100644 system/views/profiler/stats.php create mode 100644 system/views/profiler/style.css create mode 100644 vendor/autoload.php create mode 100644 vendor/bin/phpunit create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/include_paths.php create mode 100644 vendor/composer/installed.json create mode 100644 vendor/phpunit/php-code-coverage/CONTRIBUTING.md create mode 100644 vendor/phpunit/php-code-coverage/ChangeLog.markdown create mode 100644 vendor/phpunit/php-code-coverage/LICENSE create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php.in create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver/Xdebug.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Exception.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Filter.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Clover.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Factory.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/File.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/jquery.min.js create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Directory.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Iterator.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/PHP.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Text.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util/InvalidArgumentHelper.php create mode 100644 vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Version.php create mode 100644 vendor/phpunit/php-code-coverage/README.markdown create mode 100644 vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/FilterTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/CloverTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/FactoryTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/UtilTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverageTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/TestCase.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/BankAccount-clover.xml create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/BankAccount.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/BankAccountTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassExtendedTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageFunctionTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageMethodOneLineAnnotationTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageMethodTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageNoneTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPrivateTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotProtectedTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPublicTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageNothingTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoveragePrivateTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageProtectedTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoveragePublicTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoverageTwoDefaultClassAnnotations.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoveredClass.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/CoveredFunction.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageClassExtendedTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageClassTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassPublicTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageMethodTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPrivateTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotProtectedTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPublicTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePrivateTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageProtectedTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePublicTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveredClass.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/NotExistingCoveredElementTest.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/ignored-lines-clover.xml create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/source_with_ignore.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/source_with_namespace.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/source_with_oneline_annotations.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/source_without_ignore.php create mode 100644 vendor/phpunit/php-code-coverage/Tests/_files/source_without_namespace.php create mode 100644 vendor/phpunit/php-code-coverage/build.xml create mode 100644 vendor/phpunit/php-code-coverage/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/php-code-coverage/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/php-code-coverage/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/php-code-coverage/build/phpmd.xml create mode 100644 vendor/phpunit/php-code-coverage/composer.json create mode 100644 vendor/phpunit/php-code-coverage/package.xml create mode 100644 vendor/phpunit/php-code-coverage/phpunit.xml.dist create mode 100644 vendor/phpunit/php-code-coverage/scripts/auto_append.php create mode 100644 vendor/phpunit/php-code-coverage/scripts/auto_prepend.php create mode 100644 vendor/phpunit/php-file-iterator/ChangeLog.markdown create mode 100644 vendor/phpunit/php-file-iterator/File/Iterator.php create mode 100644 vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php create mode 100644 vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php.in create mode 100644 vendor/phpunit/php-file-iterator/File/Iterator/Facade.php create mode 100644 vendor/phpunit/php-file-iterator/File/Iterator/Factory.php create mode 100644 vendor/phpunit/php-file-iterator/LICENSE create mode 100644 vendor/phpunit/php-file-iterator/README.markdown create mode 100644 vendor/phpunit/php-file-iterator/build.xml create mode 100644 vendor/phpunit/php-file-iterator/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/php-file-iterator/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/php-file-iterator/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/php-file-iterator/build/phpmd.xml create mode 100644 vendor/phpunit/php-file-iterator/composer.json create mode 100644 vendor/phpunit/php-file-iterator/package-composer.json create mode 100644 vendor/phpunit/php-file-iterator/package.xml create mode 100644 vendor/phpunit/php-text-template/ChangeLog.markdown create mode 100644 vendor/phpunit/php-text-template/LICENSE create mode 100644 vendor/phpunit/php-text-template/README.markdown create mode 100644 vendor/phpunit/php-text-template/Text/Template.php create mode 100644 vendor/phpunit/php-text-template/Text/Template/Autoload.php create mode 100644 vendor/phpunit/php-text-template/Text/Template/Autoload.php.in create mode 100644 vendor/phpunit/php-text-template/build.xml create mode 100644 vendor/phpunit/php-text-template/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/php-text-template/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/php-text-template/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/php-text-template/build/phpmd.xml create mode 100644 vendor/phpunit/php-text-template/composer.json create mode 100644 vendor/phpunit/php-text-template/package-composer.json create mode 100644 vendor/phpunit/php-text-template/package.xml create mode 100644 vendor/phpunit/php-timer/ChangeLog.markdown create mode 100644 vendor/phpunit/php-timer/LICENSE create mode 100644 vendor/phpunit/php-timer/PHP/Timer.php create mode 100644 vendor/phpunit/php-timer/PHP/Timer/Autoload.php create mode 100644 vendor/phpunit/php-timer/PHP/Timer/Autoload.php.in create mode 100644 vendor/phpunit/php-timer/README.markdown create mode 100644 vendor/phpunit/php-timer/Tests/TimerTest.php create mode 100644 vendor/phpunit/php-timer/build.xml create mode 100644 vendor/phpunit/php-timer/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/php-timer/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/php-timer/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/php-timer/build/phpmd.xml create mode 100644 vendor/phpunit/php-timer/composer.json create mode 100644 vendor/phpunit/php-timer/package-composer.json create mode 100644 vendor/phpunit/php-timer/package.xml create mode 100644 vendor/phpunit/php-timer/phpunit.xml.dist create mode 100644 vendor/phpunit/php-token-stream/ChangeLog.markdown create mode 100644 vendor/phpunit/php-token-stream/LICENSE create mode 100644 vendor/phpunit/php-token-stream/PHP/Token.php create mode 100644 vendor/phpunit/php-token-stream/PHP/Token/Stream.php create mode 100644 vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php create mode 100644 vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php.in create mode 100644 vendor/phpunit/php-token-stream/PHP/Token/Stream/CachingFactory.php create mode 100644 vendor/phpunit/php-token-stream/README.markdown create mode 100644 vendor/phpunit/php-token-stream/Tests/Token/ClassTest.php create mode 100644 vendor/phpunit/php-token-stream/Tests/Token/FunctionTest.php create mode 100644 vendor/phpunit/php-token-stream/Tests/Token/IncludeTest.php create mode 100644 vendor/phpunit/php-token-stream/Tests/Token/InterfaceTest.php create mode 100644 vendor/phpunit/php-token-stream/Tests/Token/NamespaceTest.php create mode 100644 vendor/phpunit/php-token-stream/Tests/TokenTest.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/classExtendsNamespacedClass.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/classInNamespace.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/classInScopedNamespace.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/issue19.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/multipleNamespacesWithOneClassUsingBraces.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/multipleNamespacesWithOneClassUsingNonBraceSyntax.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/source.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/source2.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/source3.php create mode 100644 vendor/phpunit/php-token-stream/Tests/_files/source4.php create mode 100644 vendor/phpunit/php-token-stream/build.xml create mode 100644 vendor/phpunit/php-token-stream/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/php-token-stream/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/php-token-stream/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/php-token-stream/build/phpmd.xml create mode 100644 vendor/phpunit/php-token-stream/composer.json create mode 100644 vendor/phpunit/php-token-stream/package-composer.json create mode 100644 vendor/phpunit/php-token-stream/package.xml create mode 100644 vendor/phpunit/php-token-stream/phpunit.xml.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/CONTRIBUTING.md create mode 100644 vendor/phpunit/phpunit-mock-objects/ChangeLog.markdown create mode 100644 vendor/phpunit/phpunit-mock-objects/LICENSE create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php.in create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Identity.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Match.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Namespace.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Stub.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Object.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Static.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/InvocationMocker.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invokable.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Invocation.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/MethodName.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Parameters.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockBuilder.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockObject.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Exception.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Return.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php create mode 100644 vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Verifiable.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/GeneratorTest.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockBuilderTest.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/ObjectTest.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/StaticTest.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_clone.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_clone.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_dont_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_partial.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/interface.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_object_clone_object.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_static_clone_object.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_clone.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_clone.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_partial.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_interface.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace_starting_with_separator.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_namespace.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_partial.phpt create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/MockObjectTest.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/AbstractMockTestClass.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/AnInterface.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/FunctionCallback.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/GoogleSearch.wsdl create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/MethodCallback.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/Mockable.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/PartialMockTestClass.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/SomeClass.php create mode 100644 vendor/phpunit/phpunit-mock-objects/Tests/_files/StaticMockTestClass.php create mode 100644 vendor/phpunit/phpunit-mock-objects/build.xml create mode 100644 vendor/phpunit/phpunit-mock-objects/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/phpunit-mock-objects/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/phpunit-mock-objects/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/phpunit-mock-objects/build/phpmd.xml create mode 100644 vendor/phpunit/phpunit-mock-objects/composer.json create mode 100644 vendor/phpunit/phpunit-mock-objects/package-composer.json create mode 100644 vendor/phpunit/phpunit-mock-objects/package.xml create mode 100644 vendor/phpunit/phpunit-mock-objects/phpunit.xml.dist create mode 100644 vendor/phpunit/phpunit/CONTRIBUTING.md create mode 100644 vendor/phpunit/phpunit/ChangeLog.md create mode 100644 vendor/phpunit/phpunit/LICENSE create mode 100644 vendor/phpunit/phpunit/PHPUnit/Autoload.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Autoload.php.in create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/GroupTestSuite.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase/Logger.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestSuite.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/RepeatedTest.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/TestDecorator.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Extensions/TicketListener.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Assert.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php.in create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/AssertionFailedError.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Array.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/DOMDocument.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Double.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Exception.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/MockObject.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Numeric.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Object.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Resource.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Scalar.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/SplObjectStorage.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Type.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/ComparatorFactory.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/ComparisonFailure.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/And.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ArrayHasKey.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Attribute.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Callback.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasAttribute.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Composite.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Count.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Exception.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionCode.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionMessage.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/FileExists.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/GreaterThan.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsAnything.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEmpty.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEqual.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsFalse.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsIdentical.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsInstanceOf.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsNull.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsTrue.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsType.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/LessThan.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Not.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Or.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/PCREMatch.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/SameSize.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringContains.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringEndsWith.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringMatches.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringStartsWith.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContains.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Xor.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Error.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Error/Deprecated.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Error/Notice.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Error/Warning.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Exception.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/ExpectationFailedException.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTest.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTestError.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/OutputError.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/SelfDescribing.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTest.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestError.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestSuiteError.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/SyntheticError.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Test.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/TestFailure.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/TestListener.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite/DataProvider.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Framework/Warning.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Runner/BaseTestRunner.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Runner/StandardTestSuiteLoader.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Runner/TestSuiteLoader.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Runner/Version.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/TextUI/ResultPrinter.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Class.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Configuration.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Diff.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/ErrorHandler.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Fileloader.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Filesystem.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Filter.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Getopt.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/InvalidArgumentHelper.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Log/JSON.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Log/TAP.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/PHP.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/PHP/Default.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/PHP/Windows.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Printer.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/String.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Test.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/TestDox/NamePrettifier.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/Text.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/TestSuiteIterator.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/Type.php create mode 100644 vendor/phpunit/phpunit/PHPUnit/Util/XML.php create mode 100644 vendor/phpunit/phpunit/README.md create mode 100644 vendor/phpunit/phpunit/Tests/Extensions/RepeatedTestTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/AssertTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/ComparatorTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatches/ErrorMessageProviderTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatchesTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/ConstraintTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/SuiteTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/TestCaseTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/TestImplementorTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Framework/TestListenerTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/1021.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/1021/Issue1021Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/523.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/523/Issue523Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/578.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/578/Issue578Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/684.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/684/Issue684Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/783.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/783/ChildSuite.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/783/OneTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/783/ParentSuite.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/783/TwoTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/244.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/244/Issue244Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/322.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/322/Issue322Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/322/phpunit322.xml create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/433.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/433/Issue433Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/445.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/445/Issue445Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/503.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/503/Issue503Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/581.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/581/Issue581Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/74.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/74/Issue74Test.php create mode 100644 vendor/phpunit/phpunit/Tests/Regression/GitHub/74/NewException.php create mode 100644 vendor/phpunit/phpunit/Tests/Runner/BaseTestRunnerTest.php create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/abstract-test-class.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/concrete-test-class.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dataprovider-testdox.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/debug.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/default-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/default.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dependencies-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dependencies.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dependencies2-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dependencies2.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dependencies3-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/dependencies3.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/empty-testcase.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/exception-stack.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/exclude-group-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/exclude-group.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/failure-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/failure.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/fatal-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/fatal.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/filter-class-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/filter-class.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/filter-method-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/filter-method.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/filter-no-results.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/group-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/group.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/help.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/help2.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/list-groups.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/log-json.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/log-tap.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/log-xml.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/strict-incomplete.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/strict-isolation.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/strict.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/tap.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/test-suffix-multiple.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/test-suffix-single.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/testdox-html.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/testdox-text.phpt create mode 100644 vendor/phpunit/phpunit/Tests/TextUI/testdox.phpt create mode 100644 vendor/phpunit/phpunit/Tests/Util/ClassTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Util/ConfigurationTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Util/DiffTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Util/TestDox/NamePrettifierTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Util/TestTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Util/TypeTest.php create mode 100644 vendor/phpunit/phpunit/Tests/Util/XMLTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/AbstractTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Author.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/BankAccount.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/BankAccountTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/BankAccountTest.test.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Book.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Calculator.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ChangeCurrentWorkingDirectoryTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ClassWithNonPublicAttributes.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ClassWithToString.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ConcreteTest.my.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ConcreteTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/DataProviderTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/DependencyFailureTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/DependencySuccessTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/DependencyTestSuite.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/DoubleTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/EmptyTestCaseTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Error.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionInAssertPostConditionsTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionInAssertPreConditionsTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionInSetUpTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionInTearDownTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionInTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionNamespaceTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionStack.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ExceptionTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Failure.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/FailureTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/FatalTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/IncompleteTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/InheritedTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/JsonData/arrayObject.js create mode 100644 vendor/phpunit/phpunit/Tests/_files/JsonData/simpleObject.js create mode 100644 vendor/phpunit/phpunit/Tests/_files/JsonData/simpleObject2.js create mode 100644 vendor/phpunit/phpunit/Tests/_files/MockRunner.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/MultiDependencyTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NoArgTestCaseTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NoTestCaseClass.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NoTestCases.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NonStatic.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NotPublicTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NotVoidTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/NothingTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/OneTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/OutputTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/OverrideTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/RequirementsClassDocBlockTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/RequirementsTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/SampleClass.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/SelectorAssertionsFixture.html create mode 100644 vendor/phpunit/phpunit/Tests/_files/Singleton.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/StackTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Struct.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/Success.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/TemplateMethodsTest.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/TestIterator.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ThrowExceptionTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/ThrowNoExceptionTestCase.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/WasRun.php create mode 100644 vendor/phpunit/phpunit/Tests/_files/bar.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/configuration.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/configuration_xinclude.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/expectedFileFormat.txt create mode 100644 vendor/phpunit/phpunit/Tests/_files/foo.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/structureAttributesAreSameButValuesAreNot.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/structureExpected.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/structureIgnoreTextNodes.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/structureIsSameButDataIsNot.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/structureWrongNumberOfAttributes.xml create mode 100644 vendor/phpunit/phpunit/Tests/_files/structureWrongNumberOfNodes.xml create mode 100644 vendor/phpunit/phpunit/build.xml create mode 100644 vendor/phpunit/phpunit/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php create mode 100644 vendor/phpunit/phpunit/build/PHPCS/Sniffs/Whitespace/ConcatenationSpacingSniff.php create mode 100644 vendor/phpunit/phpunit/build/PHPCS/ruleset.xml create mode 100644 vendor/phpunit/phpunit/build/assertions.php create mode 100644 vendor/phpunit/phpunit/build/dependencies/DbUnit-1.2.1.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/File_Iterator-1.3.3.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/PHPUnit_MockObject-1.2.1.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/PHPUnit_Selenium-1.2.9.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/PHP_CodeCoverage-1.2.6.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/PHP_Invoker-1.1.2.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/PHP_Timer-1.0.4.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/PHP_TokenStream-1.1.5.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/Text_Template-1.1.3.tgz create mode 100644 vendor/phpunit/phpunit/build/dependencies/Yaml-2.1.2.tgz create mode 100644 vendor/phpunit/phpunit/build/phar-autoload.php.in create mode 100644 vendor/phpunit/phpunit/build/phpmd.xml create mode 100644 vendor/phpunit/phpunit/composer.json create mode 100644 vendor/phpunit/phpunit/composer/bin/phpunit create mode 100644 vendor/phpunit/phpunit/package.xml create mode 100644 vendor/phpunit/phpunit/phpdox.xml.dist create mode 100644 vendor/phpunit/phpunit/phpunit.bat create mode 100644 vendor/phpunit/phpunit/phpunit.php create mode 100644 vendor/phpunit/phpunit/phpunit.xml.dist create mode 100644 vendor/phpunit/phpunit/phpunit.xsd create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Dumper.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Escaper.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Inline.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/LICENSE create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Parser.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/README.md create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Tests/bootstrap.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Unescaper.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/Yaml.php create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/composer.json create mode 100644 vendor/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ba7adb4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: php + +php: + - 5.3 + +before_install: + - "git submodule update --init --recursive" + +before_script: + - "pear channel-discover pear.phing.info" + - "pear install phing/phing" + - "phpenv rehash" + - "composer install" + +script: "phing test" + +notifications: + irc: + channels: + - "irc.freenode.org#kohana" + template: + - "%{repository}/%{branch} (%{commit}) - %{author}: %{message}" + - "Build details: %{build_url}" + email: false diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a36fb76 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,14 @@ +# Kohana License Agreement + +This license is a legal agreement between you and the Kohana Team for the use of Kohana Framework (the "Software"). By obtaining the Software you agree to comply with the terms and conditions of this license. + +Copyright (c) 2007-2011 Kohana Team +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of the Kohana nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..220a934 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Kohana PHP Framework + +[Kohana](http://kohanaframework.org/) is an elegant, open source, and object oriented HMVC framework built using PHP5, by a team of volunteers. It aims to be swift, secure, and small. + +Released under a [BSD license](http://kohanaframework.org/license), Kohana can be used legally for any open source, commercial, or personal project. + +## Documentation +Kohana's documentation can be found at which also contains an API browser. + +The `userguide` module included in all Kohana releases also allows you to view the documentation locally. Once the `userguide` module is enabled in the bootstrap, it is accessible from your site via `/index.php/guide` (or just `/guide` if you are rewriting your URLs). + +## Reporting bugs +If you've stumbled across a bug, please help us out by [reporting the bug](http://dev.kohanaframework.org/projects/kohana3/) you have found. Simply log in or register and submit a new issue, leaving as much information about the bug as possible, e.g. + +* Steps to reproduce +* Expected result +* Actual result + +This will help us to fix the bug as quickly as possible, and if you'd like to fix it yourself feel free to [fork us on GitHub](https://github.com/kohana) and submit a pull request! diff --git a/application/bootstrap.php b/application/bootstrap.php new file mode 100644 index 0000000..96b8b19 --- /dev/null +++ b/application/bootstrap.php @@ -0,0 +1,129 @@ +" + */ +if (isset($_SERVER['KOHANA_ENV'])) +{ + Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV'])); +} + +/** + * Initialize Kohana, setting the default options. + * + * The following options are available: + * + * - string base_url path, and optionally domain, of your application NULL + * - string index_file name of your index file, usually "index.php" index.php + * - string charset internal character set used for input and output utf-8 + * - string cache_dir set the internal cache directory APPPATH/cache + * - integer cache_life lifetime, in seconds, of items cached 60 + * - boolean errors enable or disable error handling TRUE + * - boolean profile enable or disable internal profiling TRUE + * - boolean caching enable or disable internal caching FALSE + * - boolean expose set the X-Powered-By header FALSE + */ +Kohana::init(array( + 'base_url' => '/kohana/', +)); + +/** + * Attach the file write to logging. Multiple writers are supported. + */ +Kohana::$log->attach(new Log_File(APPPATH.'logs')); + +/** + * Attach a file reader to config. Multiple readers are supported. + */ +Kohana::$config->attach(new Config_File); + +/** + * Enable modules. Modules are referenced by a relative or absolute path. + */ +Kohana::modules(array( + // 'auth' => MODPATH.'auth', // Basic authentication + // 'cache' => MODPATH.'cache', // Caching with multiple backends + // 'codebench' => MODPATH.'codebench', // Benchmarking tool + // 'database' => MODPATH.'database', // Database access + // 'image' => MODPATH.'image', // Image manipulation + // 'minion' => MODPATH.'minion', // CLI Tasks + // 'orm' => MODPATH.'orm', // Object Relationship Mapping + // 'unittest' => MODPATH.'unittest', // Unit testing + // 'userguide' => MODPATH.'userguide', // User guide and API documentation + )); + +/** + * Set the routes. Each route must have a minimum of a name, a URI and a set of + * defaults for the URI. + */ +Route::set('default', '((/(/)))') + ->defaults(array( + 'controller' => 'welcome', + 'action' => 'index', + )); diff --git a/application/classes/Controller/Welcome.php b/application/classes/Controller/Welcome.php new file mode 100644 index 0000000..0984eff --- /dev/null +++ b/application/classes/Controller/Welcome.php @@ -0,0 +1,10 @@ +response->body('hello, world!'); + } + +} // End Welcome diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a473df1 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpunit/phpunit": "3.7.*" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3fe1de8 --- /dev/null +++ b/composer.lock @@ -0,0 +1,413 @@ +{ + "hash": "11774bed2716724738d66bb56a8a1508", + "packages": [ + { + "name": "phpunit/php-code-coverage", + "version": "1.2.6", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1.2.6" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-code-coverage/zipball/1.2.6", + "reference": "1.2.6", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-token-stream": ">=1.1.3@stable", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "time": "2012-10-16 22:34:13", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "testing", + "coverage", + "xunit" + ] + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.3", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "1.3.3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3", + "reference": "1.3.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:44:38", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "File/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "filesystem", + "iterator" + ] + }, + { + "name": "phpunit/php-text-template", + "version": "1.1.3", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-text-template.git", + "reference": "1.1.3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.3", + "reference": "1.1.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:48:39", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "template" + ] + }, + { + "name": "phpunit/php-timer", + "version": "1.0.4", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-timer.git", + "reference": "1.0.4" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4", + "reference": "1.0.4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:45:58", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "timer" + ] + }, + { + "name": "phpunit/php-token-stream", + "version": "1.1.5", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-token-stream.git", + "reference": "1.1.5" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5", + "reference": "1.1.5", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:47:14", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "tokenizer" + ] + }, + { + "name": "phpunit/phpunit", + "version": "3.7.8", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/phpunit.git", + "reference": "3.7.8" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/phpunit/zipball/3.7.8", + "reference": "3.7.8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-code-coverage": ">=1.2.1", + "phpunit/php-timer": ">=1.0.2", + "phpunit/phpunit-mock-objects": ">=1.2.0", + "symfony/yaml": ">=2.1.0", + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*" + }, + "suggest": { + "phpunit/php-invoker": ">=1.1.0", + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*" + }, + "time": "2012-10-16 22:37:08", + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "testing", + "phpunit", + "xunit" + ] + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.1", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "1.2.1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/zipball/1.2.1", + "reference": "1.2.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable", + "ext-reflection": "*", + "ext-spl": "*" + }, + "suggest": { + "ext-soap": "*" + }, + "time": "2012-10-05 00:00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "mock", + "xunit" + ] + }, + { + "name": "symfony/yaml", + "version": "v2.1.2", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml", + "reference": "v2.1.0-RC2" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony/Yaml/zipball/v2.1.0-RC2", + "reference": "v2.1.0-RC2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-08-22 06:48:41", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml": "" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com" + } + ], + "packages-dev": null, + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": [ + + ] +} diff --git a/composer.phar b/composer.phar new file mode 100644 index 0000000000000000000000000000000000000000..7c838090f118dec3740491a8e73eb5695a91a314 GIT binary patch literal 628852 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%0A@7eaI;k>%f6rzV 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 NV}KpU;q9>SA~-(L{H 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

Sw)>q}eC&tdn6;Y02 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 CDkT2^ literal 0 HcmV?d00001 diff --git a/example.htaccess b/example.htaccess new file mode 100644 index 0000000..53b8ccb --- /dev/null +++ b/example.htaccess @@ -0,0 +1,21 @@ +# Turn on URL rewriting +RewriteEngine On + +# Installation directory +RewriteBase / + +# Protect hidden files from being viewed + + Order Deny,Allow + Deny From All + + +# Protect application and system files from being viewed +RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L] + +# Allow any files or directories that exist to be displayed directly +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d + +# Rewrite all other URLs to index.php/URL +RewriteRule .* index.php/$0 [PT] diff --git a/index.php b/index.php new file mode 100644 index 0000000..6c352aa --- /dev/null +++ b/index.php @@ -0,0 +1,121 @@ += 5.3, it is recommended to disable + * deprecated notices. Disable with: E_ALL & ~E_DEPRECATED + */ +error_reporting(E_ALL | E_STRICT); + +/** + * End of standard configuration! Changing any of the code below should only be + * attempted by those with a working knowledge of Kohana internals. + * + * @link http://kohanaframework.org/guide/using.configuration + */ + +// Set the full path to the docroot +define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR); + +// Make the application relative to the docroot, for symlink'd index.php +if ( ! is_dir($application) AND is_dir(DOCROOT.$application)) + $application = DOCROOT.$application; + +// Make the modules relative to the docroot, for symlink'd index.php +if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules)) + $modules = DOCROOT.$modules; + +// Make the system relative to the docroot, for symlink'd index.php +if ( ! is_dir($system) AND is_dir(DOCROOT.$system)) + $system = DOCROOT.$system; + +// Define the absolute paths for configured directories +define('APPPATH', realpath($application).DIRECTORY_SEPARATOR); +define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR); +define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR); + +// Clean up the configuration vars +unset($application, $modules, $system); + +if (file_exists('install'.EXT)) +{ + // Load the installation check + return include 'install'.EXT; +} + +/** + * Define the start time of the application, used for profiling. + */ +if ( ! defined('KOHANA_START_TIME')) +{ + define('KOHANA_START_TIME', microtime(TRUE)); +} + +/** + * Define the memory usage at the start of the application, used for profiling. + */ +if ( ! defined('KOHANA_START_MEMORY')) +{ + define('KOHANA_START_MEMORY', memory_get_usage()); +} + +// Bootstrap the application +require APPPATH.'bootstrap'.EXT; + +if (PHP_SAPI == 'cli') // Try and load minion +{ + class_exists('Minion_Task') OR die('Please enable the Minion module for CLI support.'); + set_exception_handler(array('Minion_Exception', 'handler')); + + Minion_Task::factory(Minion_CLI::options())->execute(); +} +else +{ + /** + * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. + * If no source is specified, the URI will be automatically detected. + */ + echo Request::factory(TRUE, array(), FALSE) + ->execute() + ->send_headers(TRUE) + ->body(); +} diff --git a/install.php b/install.php new file mode 100644 index 0000000..f654177 --- /dev/null +++ b/install.php @@ -0,0 +1,233 @@ + + + + + + + + Kohana Installation + + + + + + +

Environment Tests

+ +

+ The following tests have been run to determine if Kohana will work in your environment. + If any of the tests have failed, consult the documentation + for more information on how to correct the problem. +

+ + + + + + + =')): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PHP VersionKohana requires PHP 5.3.3 or newer, this version is .
System DirectoryThe configured system directory does not exist or does not contain required files.
Application DirectoryThe configured application directory does not exist or does not contain required files.
Cache DirectoryThe directory is not writable.
Logs DirectoryThe directory is not writable.
PCRE UTF-8PCRE has not been compiled with UTF-8 support.PCRE has not been compiled with Unicode property support.Pass
SPL EnabledPassPHP SPL is either not loaded or not compiled in.
Reflection EnabledPassPHP reflection is either not loaded or not compiled in.
Filters EnabledPassThe filter extension is either not loaded or not compiled in.
Iconv Extension LoadedPassThe iconv extension is not loaded.
Mbstring Not OverloadedThe mbstring extension is overloading PHP's native string functions.Pass
Character Type (CTYPE) ExtensionThe ctype extension is not enabled.Pass
URI DeterminationPassNeither $_SERVER['REQUEST_URI'], $_SERVER['PHP_SELF'], or $_SERVER['PATH_INFO'] is available.
+ + +

✘ Kohana may not work correctly with your environment.

+ +

✔ Your environment passed all requirements.
+ Remove or rename the install file now.

+ + +

Optional Tests

+ +

+ The following extensions are not required to run the Kohana core, but if enabled can provide access to additional classes. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PECL HTTP EnabledPassKohana can use the http extension for the Request_Client_External class.
cURL EnabledPassKohana can use the cURL extension for the Request_Client_External class.
mcrypt EnabledPassKohana requires mcrypt for the Encrypt class.
GD EnabledPassKohana requires GD v2 for the Image class.
MySQL EnabledPassKohana can use the MySQL extension to support MySQL databases.
PDO EnabledPassKohana can use PDO to support additional databases.
+ + + diff --git a/modules/auth/README.md b/modules/auth/README.md new file mode 100644 index 0000000..27ac9cd --- /dev/null +++ b/modules/auth/README.md @@ -0,0 +1,13 @@ +New Age Auth +--- + +I've forked the main Auth module because there were some fundamental flaws with it: + + 1. It's trivial to [bruteforce](http://dev.kohanaframework.org/issues/3163) publicly hidden salt hashes. + - I've fixed this by switching the password hashing algorithm to the more secure secret-key based hash_hmac method. + 2. ORM drivers were included. + - I've fixed this by simply removing them. They cause confusion with new users because they think that Auth requires ORM. The only driver currently provided by default is the file driver. + 3. Auth::get_user()'s api is inconsistent because it returns different data types. + - I've fixed this by returning an empty user model by default. You can override what gets returned (if you've changed your user model class name for instance) by overloading the get_user() method in your application. + +These changes should be merged into the mainline branch eventually, but they completely break the API, so likely won't be done until 3.1. \ No newline at end of file diff --git a/modules/auth/classes/Auth.php b/modules/auth/classes/Auth.php new file mode 100644 index 0000000..8d31fad --- /dev/null +++ b/modules/auth/classes/Auth.php @@ -0,0 +1,3 @@ +load('auth'); + + if ( ! $type = $config->get('driver')) + { + $type = 'file'; + } + + // Set the session class name + $class = 'Auth_'.ucfirst($type); + + // Create a new session instance + Auth::$_instance = new $class($config); + } + + return Auth::$_instance; + } + + protected $_session; + + protected $_config; + + /** + * Loads Session and configuration options. + * + * @param array $config Config Options + * @return void + */ + public function __construct($config = array()) + { + // Save the config in the object + $this->_config = $config; + + $this->_session = Session::instance($this->_config['session_type']); + } + + abstract protected function _login($username, $password, $remember); + + abstract public function password($username); + + abstract public function check_password($password); + + /** + * Gets the currently logged in user from the session. + * Returns NULL if no user is currently logged in. + * + * @param mixed $default Default value to return if the user is currently not logged in. + * @return mixed + */ + public function get_user($default = NULL) + { + return $this->_session->get($this->_config['session_key'], $default); + } + + /** + * Attempt to log in a user by using an ORM object and plain-text password. + * + * @param string $username Username to log in + * @param string $password Password to check against + * @param boolean $remember Enable autologin + * @return boolean + */ + public function login($username, $password, $remember = FALSE) + { + if (empty($password)) + return FALSE; + + return $this->_login($username, $password, $remember); + } + + /** + * Log out a user by removing the related session variables. + * + * @param boolean $destroy Completely destroy the session + * @param boolean $logout_all Remove all tokens for user + * @return boolean + */ + public function logout($destroy = FALSE, $logout_all = FALSE) + { + if ($destroy === TRUE) + { + // Destroy the session completely + $this->_session->destroy(); + } + else + { + // Remove the user from the session + $this->_session->delete($this->_config['session_key']); + + // Regenerate session_id + $this->_session->regenerate(); + } + + // Double check + return ! $this->logged_in(); + } + + /** + * Check if there is an active session. Optionally allows checking for a + * specific role. + * + * @param string $role role name + * @return mixed + */ + public function logged_in($role = NULL) + { + return ($this->get_user() !== NULL); + } + + /** + * Creates a hashed hmac password from a plaintext password. This + * method is deprecated, [Auth::hash] should be used instead. + * + * @deprecated + * @param string $password Plaintext password + */ + public function hash_password($password) + { + return $this->hash($password); + } + + /** + * Perform a hmac hash, using the configured method. + * + * @param string $str string to hash + * @return string + */ + public function hash($str) + { + if ( ! $this->_config['hash_key']) + throw new Kohana_Exception('A valid hash key must be set in your auth config.'); + + return hash_hmac($this->_config['hash_method'], $str, $this->_config['hash_key']); + } + + protected function complete_login($user) + { + // Regenerate session_id + $this->_session->regenerate(); + + // Store username in session + $this->_session->set($this->_config['session_key'], $user); + + return TRUE; + } + +} // End Auth diff --git a/modules/auth/classes/Kohana/Auth/File.php b/modules/auth/classes/Kohana/Auth/File.php new file mode 100644 index 0000000..8640fde --- /dev/null +++ b/modules/auth/classes/Kohana/Auth/File.php @@ -0,0 +1,94 @@ +_users = Arr::get($config, 'users', array()); + } + + /** + * Logs a user in. + * + * @param string $username Username + * @param string $password Password + * @param boolean $remember Enable autologin (not supported) + * @return boolean + */ + protected function _login($username, $password, $remember) + { + if (is_string($password)) + { + // Create a hashed password + $password = $this->hash($password); + } + + if (isset($this->_users[$username]) AND $this->_users[$username] === $password) + { + // Complete the login + return $this->complete_login($username); + } + + // Login failed + return FALSE; + } + + /** + * Forces a user to be logged in, without specifying a password. + * + * @param mixed $username Username + * @return boolean + */ + public function force_login($username) + { + // Complete the login + return $this->complete_login($username); + } + + /** + * Get the stored password for a username. + * + * @param mixed $username Username + * @return string + */ + public function password($username) + { + return Arr::get($this->_users, $username, FALSE); + } + + /** + * Compare password with original (plain text). Works for current (logged in) user + * + * @param string $password Password + * @return boolean + */ + public function check_password($password) + { + $username = $this->get_user(); + + if ($username === FALSE) + { + return FALSE; + } + + return ($password === $this->password($username)); + } + +} // End Auth File diff --git a/modules/auth/config/auth.php b/modules/auth/config/auth.php new file mode 100644 index 0000000..837930d --- /dev/null +++ b/modules/auth/config/auth.php @@ -0,0 +1,17 @@ + 'File', + 'hash_method' => 'sha256', + 'hash_key' => NULL, + 'lifetime' => 1209600, + 'session_type' => Session::$default, + 'session_key' => 'auth_user', + + // Username/password combinations for the Auth File driver + 'users' => array( + // 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02', + ), + +); diff --git a/modules/auth/config/userguide.php b/modules/auth/config/userguide.php new file mode 100644 index 0000000..425e27c --- /dev/null +++ b/modules/auth/config/userguide.php @@ -0,0 +1,23 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'auth' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'Auth', + + // A short description of this module, shown on the index page + 'description' => 'User authentication and authorization.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2012 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/modules/auth/guide/auth/config.md b/modules/auth/guide/auth/config.md new file mode 100644 index 0000000..e411fa0 --- /dev/null +++ b/modules/auth/guide/auth/config.md @@ -0,0 +1,13 @@ +# Configuration + +The default configuration file is located in `MODPATH/auth/config/auth.php`. You should copy this file to `APPPATH/config/auth.php` and make changes there, in keeping with the [cascading filesystem](../kohana/files). + +[Config merging](../kohana/config#config-merging) allows these default configuration settings to apply if you don't overwrite them in your application configuration file. + +Name | Type | Default | Description +-----|------|---------|------------ +driver | `string` | file | The name of the auth driver to use. +hash_method | `string` | sha256 | The hashing function to use on the passwords. +hash_key | `string` | NULL | The key to use when hashing the password. +session_type | `string` | [Session::$default] | The type of session to use when storing the auth user. +session_key | `string` | auth_user | The name of the session variable used to save the user. diff --git a/modules/auth/guide/auth/driver/develop.md b/modules/auth/guide/auth/driver/develop.md new file mode 100644 index 0000000..a69a08c --- /dev/null +++ b/modules/auth/guide/auth/driver/develop.md @@ -0,0 +1,79 @@ +# Developing Drivers + +## Real World Example + +Sometimes the best way to learn is to jump right in and read the code from another module. The [ORM](https://github.com/kohana/orm/blob/3.2/develop/classes/kohana/auth/orm.php) module comes with an auth driver you can learn from. + +[!!] We will be developing an `example` driver. In your own driver you will substitute `example` with your driver name. + +This example file would be saved at `APPPATH/classes/auth/example.php` (or `MODPATH` if you are creating a module). + +--- + +## Quick Example + +First we will show you a quick example and then break down what is going on. + +~~~ +class Auth_Example extends Auth +{ + protected function _login($username, $password, $remember) + { + // Do username/password check here + } + + public function password($username) + { + // Return the password for the username + } + + public function check_password($password) + { + // Check to see if the logged in user has the given password + } + + public function logged_in($role = NULL) + { + // Check to see if the user is logged in, and if $role is set, has all roles + } + + public function get_user($default = NULL) + { + // Get the logged in user, or return the $default if a user is not found + } +} +~~~ + +## Extending Auth + +All drivers must extend the [Auth] class. + + class Auth_Example extends Auth + +## Abstract Methods + +The `Auth` class has 3 abstract methods that must be defined in your new driver. + +~~~ +abstract protected function _login($username, $password, $remember); + +abstract public function password($username); + +abstract public function check_password($user); +~~~ + +## Extending Functionality + +Given that every auth system is going to check if users exist and if they have roles or not you will more than likely have to change some default functionality. + +Here are a few functions that you should pay attention to. + +~~~ +public function logged_in($role = NULL) + +public function get_user($default = NULL) +~~~ + +## Activating the Driver + +After you create your driver you will want to use it. It is a easy as setting the `driver` [configuration](config) option to the name of your driver (in our case `example`). diff --git a/modules/auth/guide/auth/driver/file.md b/modules/auth/guide/auth/driver/file.md new file mode 100644 index 0000000..7a6fa09 --- /dev/null +++ b/modules/auth/guide/auth/driver/file.md @@ -0,0 +1,19 @@ +# File Driver + +The [Auth::File] driver is included with the auth module. + +Below are additional configuration options that can be set for this driver. + +Name | Type | Default | Description +-----|------|---------|------------- +users | `array` | array() | A user => password (_hashed_) array of all the users in your application + +## Forcing Login + +[Auth_File::force_login] allows you to force a user login without a password. + +~~~ +// Force the user with a username of admin to be logged into your application +Auth::instance()->force_login('admin'); +$user = Auth::instance()->get_user(); +~~~ diff --git a/modules/auth/guide/auth/index.md b/modules/auth/guide/auth/index.md new file mode 100644 index 0000000..18127e6 --- /dev/null +++ b/modules/auth/guide/auth/index.md @@ -0,0 +1,19 @@ +# Auth + +User authentication and authorization is provided by the auth module. + +The auth module is included with Kohana, but needs to be enabled before you can use it. To enable, open your `application/bootstrap.php` file and modify the call to [Kohana::modules] by including the auth module like so: + +~~~ +Kohana::modules(array( + ... + 'auth' => MODPATH.'auth', + ... +)); +~~~ + +Next, you will then need to [configure](config) the auth module. + +The auth module provides the [Auth::File] driver for you. There is also an auth driver included with the ORM module. + +As your application needs change you may need to find another driver or [develop](driver/develop) your own. diff --git a/modules/auth/guide/auth/login.md b/modules/auth/guide/auth/login.md new file mode 100644 index 0000000..1207b5d --- /dev/null +++ b/modules/auth/guide/auth/login.md @@ -0,0 +1,62 @@ +# Log in and out + +The auth module provides methods to help you log users in and out of your application. + +## Log in + +The [Auth::login] method handles the login. + +~~~ +// Handled from a form with inputs with names email / password +$post = $this->request->post(); +$success = Auth::instance()->login($post['email'], $post['password']); + +if ($success) +{ + // Login successful, send to app +} +else +{ + // Login failed, send back to form with error message +} +~~~ + +## Logged in User + +There are two ways to check if a user is logged in. If you just need to check if the user is logged in use [Auth::logged_in]. + +~~~ +if (Auth::instance()->logged_in()) +{ + // User is logged in, continue on +} +else +{ + // User isn't logged in, redirect to the login form. +} +~~~ + +You can also get the logged in user object by using [Auth::get_user]. If the user is null, then no user was found. + +~~~ +$user = Auth::instance()->get_user(); + +// Check for a user (NULL if not user is found) +if ($user !== null) +{ + // User is found, continue on +} +else +{ + // User was not found, redirect to the login form +} +~~~ + +## Log out + +The [Auth::logout] method will take care of logging out a user. + +~~~ +Auth::instance()->logout(); +// Redirect the user back to login page +~~~ diff --git a/modules/auth/guide/auth/menu.md b/modules/auth/guide/auth/menu.md new file mode 100644 index 0000000..23fc0ee --- /dev/null +++ b/modules/auth/guide/auth/menu.md @@ -0,0 +1,6 @@ +## [Auth]() +- [Configuration](config) +- [Log in and out](login) +- Drivers + - [File](driver/file) + - [Developing](driver/develop) diff --git a/modules/cache/README.md b/modules/cache/README.md new file mode 100644 index 0000000..7acff9c --- /dev/null +++ b/modules/cache/README.md @@ -0,0 +1,59 @@ +Kohana Cache library +==================== + +The cache library for Kohana 3 provides a simple interface to the most common cache solutions. Developers are free to add their own caching solutions that follow the cache design pattern defined within this module. + +Supported cache solutions +------------------------- + +Currently this module supports the following cache methods. + +1. APC +2. Memcache +3. Memcached-tags (Supports tags) +4. SQLite (Supports tags) +5. File +6. Wincache + +Planned support +--------------- + +In the near future, additional support for the following methods will be included. + +1. Memcached + +Introduction to caching +----------------------- + +To use caching to the maximum potential, your application should be designed with caching in mind from the outset. In general, the most effective caches contain lots of small collections of data that are the result of expensive computational operations, such as searching through a large data set. + +There are many different caching methods available for PHP, from the very basic file based caching to opcode caching in eAccelerator and APC. Caching engines that use physical memory over disk based storage are always faster, however many do not support more advanced features such as tagging. + +Using Cache +----------- + +To use Kohana Cache, download and extract the latest stable release of Kohana Cache from [Github](http://github.com/samsoir/kohana-cache). Place the module into your Kohana instances modules folder. Finally enable the module within the application bootstrap within the section entitled _modules_. + +Quick example +------------- + +The following is a quick example of how to use Kohana Cache. The example is using the SQLite driver. + + 'bar', 'apples' => 'pear', 'BDFL' => 'Shadowhand'); + + // Save the data to cache, with an id of test_id and a lifetime of 10 minutes + $mycache->set('test_id', $data, 600); + + // Retrieve the data from cache + $retrieved_data = $mycache->get('test_id'); + + // Remove the cache item + $mycache->delete('test_id'); + + // Clear the cache of all stored items + $mycache->delete_all(); diff --git a/modules/cache/classes/Cache.php b/modules/cache/classes/Cache.php new file mode 100644 index 0000000..2b43c93 --- /dev/null +++ b/modules/cache/classes/Cache.php @@ -0,0 +1,3 @@ + array( // Default group + * 'driver' => 'memcache', // using Memcache driver + * 'servers' => array( // Available server definitions + * array( + * 'host' => 'localhost', + * 'port' => 11211, + * 'persistent' => FALSE + * ) + * ), + * 'compression' => FALSE, // Use compression? + * ), + * ) + * + * In cases where only one cache group is required, if the group is named `default` there is + * no need to pass the group name when instantiating a cache instance. + * + * #### General cache group configuration settings + * + * Below are the settings available to all types of cache driver. + * + * Name | Required | Description + * -------------- | -------- | --------------------------------------------------------------- + * driver | __YES__ | (_string_) The driver type to use + * + * Details of the settings specific to each driver are available within the drivers documentation. + * + * ### System requirements + * + * * Kohana 3.0.x + * * PHP 5.2.4 or greater + * + * @package Kohana/Cache + * @category Base + * @version 2.0 + * @author Kohana Team + * @copyright (c) 2009-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +abstract class Kohana_Cache { + + const DEFAULT_EXPIRE = 3600; + + /** + * @var string default driver to use + */ + public static $default = 'file'; + + /** + * @var Kohana_Cache instances + */ + public static $instances = array(); + + /** + * Creates a singleton of a Kohana Cache group. If no group is supplied + * the __default__ cache group is used. + * + * // Create an instance of the default group + * $default_group = Cache::instance(); + * + * // Create an instance of a group + * $foo_group = Cache::instance('foo'); + * + * // Access an instantiated group directly + * $foo_group = Cache::$instances['default']; + * + * @param string $group the name of the cache group to use [Optional] + * @return Cache + * @throws Cache_Exception + */ + public static function instance($group = NULL) + { + // If there is no group supplied + if ($group === NULL) + { + // Use the default setting + $group = Cache::$default; + } + + if (isset(Cache::$instances[$group])) + { + // Return the current group if initiated already + return Cache::$instances[$group]; + } + + $config = Kohana::$config->load('cache'); + + if ( ! $config->offsetExists($group)) + { + throw new Cache_Exception( + 'Failed to load Kohana Cache group: :group', + array(':group' => $group) + ); + } + + $config = $config->get($group); + + // Create a new cache type instance + $cache_class = 'Cache_'.ucfirst($config['driver']); + Cache::$instances[$group] = new $cache_class($config); + + // Return the instance + return Cache::$instances[$group]; + } + + /** + * @var Config + */ + protected $_config = array(); + + /** + * Ensures singleton pattern is observed, loads the default expiry + * + * @param array $config configuration + */ + protected function __construct(array $config) + { + $this->config($config); + } + + /** + * Getter and setter for the configuration. If no argument provided, the + * current configuration is returned. Otherwise the configuration is set + * to this class. + * + * // Overwrite all configuration + * $cache->config(array('driver' => 'memcache', '...')); + * + * // Set a new configuration setting + * $cache->config('servers', array( + * 'foo' => 'bar', + * '...' + * )); + * + * // Get a configuration setting + * $servers = $cache->config('servers); + * + * @param mixed key to set to array, either array or config path + * @param mixed value to associate with key + * @return mixed + */ + public function config($key = NULL, $value = NULL) + { + if ($key === NULL) + return $this->_config; + + if (is_array($key)) + { + $this->_config = $key; + } + else + { + if ($value === NULL) + return Arr::get($this->_config, $key); + + $this->_config[$key] = $value; + } + + return $this; + } + + /** + * Overload the __clone() method to prevent cloning + * + * @return void + * @throws Cache_Exception + */ + final public function __clone() + { + throw new Cache_Exception('Cloning of Kohana_Cache objects is forbidden'); + } + + /** + * Retrieve a cached value entry by id. + * + * // Retrieve cache entry from default group + * $data = Cache::instance()->get('foo'); + * + * // Retrieve cache entry from default group and return 'bar' if miss + * $data = Cache::instance()->get('foo', 'bar'); + * + * // Retrieve cache entry from memcache group + * $data = Cache::instance('memcache')->get('foo'); + * + * @param string $id id of cache to entry + * @param string $default default value to return if cache miss + * @return mixed + * @throws Cache_Exception + */ + abstract public function get($id, $default = NULL); + + /** + * Set a value to cache with id and lifetime + * + * $data = 'bar'; + * + * // Set 'bar' to 'foo' in default group, using default expiry + * Cache::instance()->set('foo', $data); + * + * // Set 'bar' to 'foo' in default group for 30 seconds + * Cache::instance()->set('foo', $data, 30); + * + * // Set 'bar' to 'foo' in memcache group for 10 minutes + * if (Cache::instance('memcache')->set('foo', $data, 600)) + * { + * // Cache was set successfully + * return + * } + * + * @param string $id id of cache entry + * @param string $data data to set to cache + * @param integer $lifetime lifetime in seconds + * @return boolean + */ + abstract public function set($id, $data, $lifetime = 3600); + + /** + * Delete a cache entry based on id + * + * // Delete 'foo' entry from the default group + * Cache::instance()->delete('foo'); + * + * // Delete 'foo' entry from the memcache group + * Cache::instance('memcache')->delete('foo') + * + * @param string $id id to remove from cache + * @return boolean + */ + abstract public function delete($id); + + /** + * Delete all cache entries. + * + * Beware of using this method when + * using shared memory cache systems, as it will wipe every + * entry within the system for all clients. + * + * // Delete all cache entries in the default group + * Cache::instance()->delete_all(); + * + * // Delete all cache entries in the memcache group + * Cache::instance('memcache')->delete_all(); + * + * @return boolean + */ + abstract public function delete_all(); + + /** + * Replaces troublesome characters with underscores. + * + * // Sanitize a cache id + * $id = $this->_sanitize_id($id); + * + * @param string $id id of cache to sanitize + * @return string + */ + protected function _sanitize_id($id) + { + // Change slashes and spaces to underscores + return str_replace(array('/', '\\', ' '), '_', $id); + } +} +// End Kohana_Cache diff --git a/modules/cache/classes/Kohana/Cache/Apc.php b/modules/cache/classes/Kohana/Cache/Apc.php new file mode 100644 index 0000000..acefcc8 --- /dev/null +++ b/modules/cache/classes/Kohana/Cache/Apc.php @@ -0,0 +1,166 @@ + array( // Driver group + * 'driver' => 'apc', // using APC driver + * ), + * ) + * + * In cases where only one cache group is required, if the group is named `default` there is + * no need to pass the group name when instantiating a cache instance. + * + * #### General cache group configuration settings + * + * Below are the settings available to all types of cache driver. + * + * Name | Required | Description + * -------------- | -------- | --------------------------------------------------------------- + * driver | __YES__ | (_string_) The driver type to use + * + * ### System requirements + * + * * Kohana 3.0.x + * * PHP 5.2.4 or greater + * * APC PHP extension + * + * @package Kohana/Cache + * @category Base + * @author Kohana Team + * @copyright (c) 2009-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_Cache_Apc extends Cache implements Cache_Arithmetic { + + /** + * Check for existence of the APC extension This method cannot be invoked externally. The driver must + * be instantiated using the `Cache::instance()` method. + * + * @param array $config configuration + * @throws Cache_Exception + */ + protected function __construct(array $config) + { + if ( ! extension_loaded('apc')) + { + throw new Cache_Exception('PHP APC extension is not available.'); + } + + parent::__construct($config); + } + + /** + * Retrieve a cached value entry by id. + * + * // Retrieve cache entry from apc group + * $data = Cache::instance('apc')->get('foo'); + * + * // Retrieve cache entry from apc group and return 'bar' if miss + * $data = Cache::instance('apc')->get('foo', 'bar'); + * + * @param string $id id of cache to entry + * @param string $default default value to return if cache miss + * @return mixed + * @throws Cache_Exception + */ + public function get($id, $default = NULL) + { + $data = apc_fetch($this->_sanitize_id($id), $success); + + return $success ? $data : $default; + } + + /** + * Set a value to cache with id and lifetime + * + * $data = 'bar'; + * + * // Set 'bar' to 'foo' in apc group, using default expiry + * Cache::instance('apc')->set('foo', $data); + * + * // Set 'bar' to 'foo' in apc group for 30 seconds + * Cache::instance('apc')->set('foo', $data, 30); + * + * @param string $id id of cache entry + * @param string $data data to set to cache + * @param integer $lifetime lifetime in seconds + * @return boolean + */ + public function set($id, $data, $lifetime = NULL) + { + if ($lifetime === NULL) + { + $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); + } + + return apc_store($this->_sanitize_id($id), $data, $lifetime); + } + + /** + * Delete a cache entry based on id + * + * // Delete 'foo' entry from the apc group + * Cache::instance('apc')->delete('foo'); + * + * @param string $id id to remove from cache + * @return boolean + */ + public function delete($id) + { + return apc_delete($this->_sanitize_id($id)); + } + + /** + * Delete all cache entries. + * + * Beware of using this method when + * using shared memory cache systems, as it will wipe every + * entry within the system for all clients. + * + * // Delete all cache entries in the apc group + * Cache::instance('apc')->delete_all(); + * + * @return boolean + */ + public function delete_all() + { + return apc_clear_cache('user'); + } + + /** + * Increments a given value by the step value supplied. + * Useful for shared counters and other persistent integer based + * tracking. + * + * @param string id of cache entry to increment + * @param int step value to increment by + * @return integer + * @return boolean + */ + public function increment($id, $step = 1) + { + return apc_inc($id, $step); + } + + /** + * Decrements a given value by the step value supplied. + * Useful for shared counters and other persistent integer based + * tracking. + * + * @param string id of cache entry to decrement + * @param int step value to decrement by + * @return integer + * @return boolean + */ + public function decrement($id, $step = 1) + { + return apc_dec($id, $step); + } + +} // End Kohana_Cache_Apc diff --git a/modules/cache/classes/Kohana/Cache/Arithmetic.php b/modules/cache/classes/Kohana/Cache/Arithmetic.php new file mode 100644 index 0000000..1bdfb31 --- /dev/null +++ b/modules/cache/classes/Kohana/Cache/Arithmetic.php @@ -0,0 +1,39 @@ + array( // File driver group + * 'driver' => 'file', // using File driver + * 'cache_dir' => APPPATH.'cache/.kohana_cache', // Cache location + * ), + * ) + * + * In cases where only one cache group is required, if the group is named `default` there is + * no need to pass the group name when instantiating a cache instance. + * + * #### General cache group configuration settings + * + * Below are the settings available to all types of cache driver. + * + * Name | Required | Description + * -------------- | -------- | --------------------------------------------------------------- + * driver | __YES__ | (_string_) The driver type to use + * cache_dir | __NO__ | (_string_) The cache directory to use for this cache instance + * + * ### System requirements + * + * * Kohana 3.0.x + * * PHP 5.2.4 or greater + * + * @package Kohana/Cache + * @category Base + * @author Kohana Team + * @copyright (c) 2009-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_Cache_File extends Cache implements Cache_GarbageCollect { + + /** + * Creates a hashed filename based on the string. This is used + * to create shorter unique IDs for each cache filename. + * + * // Create the cache filename + * $filename = Cache_File::filename($this->_sanitize_id($id)); + * + * @param string $string string to hash into filename + * @return string + */ + protected static function filename($string) + { + return sha1($string).'.cache'; + } + + /** + * @var string the caching directory + */ + protected $_cache_dir; + + /** + * Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must + * be instantiated using the `Cache::instance()` method. + * + * @param array $config config + * @throws Cache_Exception + */ + protected function __construct(array $config) + { + // Setup parent + parent::__construct($config); + + try + { + $directory = Arr::get($this->_config, 'cache_dir', Kohana::$cache_dir); + $this->_cache_dir = new SplFileInfo($directory); + } + // PHP < 5.3 exception handle + catch (ErrorException $e) + { + $this->_cache_dir = $this->_make_directory($directory, 0777, TRUE); + } + // PHP >= 5.3 exception handle + catch (UnexpectedValueException $e) + { + $this->_cache_dir = $this->_make_directory($directory, 0777, TRUE); + } + + // If the defined directory is a file, get outta here + if ($this->_cache_dir->isFile()) + { + throw new Cache_Exception('Unable to create cache directory as a file already exists : :resource', array(':resource' => $this->_cache_dir->getRealPath())); + } + + // Check the read status of the directory + if ( ! $this->_cache_dir->isReadable()) + { + throw new Cache_Exception('Unable to read from the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath())); + } + + // Check the write status of the directory + if ( ! $this->_cache_dir->isWritable()) + { + throw new Cache_Exception('Unable to write to the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath())); + } + } + + /** + * Retrieve a cached value entry by id. + * + * // Retrieve cache entry from file group + * $data = Cache::instance('file')->get('foo'); + * + * // Retrieve cache entry from file group and return 'bar' if miss + * $data = Cache::instance('file')->get('foo', 'bar'); + * + * @param string $id id of cache to entry + * @param string $default default value to return if cache miss + * @return mixed + * @throws Cache_Exception + */ + public function get($id, $default = NULL) + { + $filename = Cache_File::filename($this->_sanitize_id($id)); + $directory = $this->_resolve_directory($filename); + + // Wrap operations in try/catch to handle notices + try + { + // Open file + $file = new SplFileInfo($directory.$filename); + + // If file does not exist + if ( ! $file->isFile()) + { + // Return default value + return $default; + } + else + { + // Open the file and parse data + $created = $file->getMTime(); + $data = $file->openFile(); + $lifetime = $data->fgets(); + + // If we're at the EOF at this point, corrupted! + if ($data->eof()) + { + throw new Cache_Exception(__METHOD__.' corrupted cache file!'); + } + + $cache = ''; + + while ($data->eof() === FALSE) + { + $cache .= $data->fgets(); + } + + // Test the expiry + if (($created + (int) $lifetime) < time()) + { + // Delete the file + $this->_delete_file($file, NULL, TRUE); + return $default; + } + else + { + return unserialize($cache); + } + } + + } + catch (ErrorException $e) + { + // Handle ErrorException caused by failed unserialization + if ($e->getCode() === E_NOTICE) + { + throw new Cache_Exception(__METHOD__.' failed to unserialize cached object with message : '.$e->getMessage()); + } + + // Otherwise throw the exception + throw $e; + } + } + + /** + * Set a value to cache with id and lifetime + * + * $data = 'bar'; + * + * // Set 'bar' to 'foo' in file group, using default expiry + * Cache::instance('file')->set('foo', $data); + * + * // Set 'bar' to 'foo' in file group for 30 seconds + * Cache::instance('file')->set('foo', $data, 30); + * + * @param string $id id of cache entry + * @param string $data data to set to cache + * @param integer $lifetime lifetime in seconds + * @return boolean + */ + public function set($id, $data, $lifetime = NULL) + { + $filename = Cache_File::filename($this->_sanitize_id($id)); + $directory = $this->_resolve_directory($filename); + + // If lifetime is NULL + if ($lifetime === NULL) + { + // Set to the default expiry + $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); + } + + // Open directory + $dir = new SplFileInfo($directory); + + // If the directory path is not a directory + if ( ! $dir->isDir()) + { + // Create the directory + if ( ! mkdir($directory, 0777, TRUE)) + { + throw new Cache_Exception(__METHOD__.' unable to create directory : :directory', array(':directory' => $directory)); + } + + // chmod to solve potential umask issues + chmod($directory, 0777); + } + + // Open file to inspect + $resouce = new SplFileInfo($directory.$filename); + $file = $resouce->openFile('w'); + + try + { + $data = $lifetime."\n".serialize($data); + $file->fwrite($data, strlen($data)); + return (bool) $file->fflush(); + } + catch (ErrorException $e) + { + // If serialize through an error exception + if ($e->getCode() === E_NOTICE) + { + // Throw a caching error + throw new Cache_Exception(__METHOD__.' failed to serialize data for caching with message : '.$e->getMessage()); + } + + // Else rethrow the error exception + throw $e; + } + } + + /** + * Delete a cache entry based on id + * + * // Delete 'foo' entry from the file group + * Cache::instance('file')->delete('foo'); + * + * @param string $id id to remove from cache + * @return boolean + */ + public function delete($id) + { + $filename = Cache_File::filename($this->_sanitize_id($id)); + $directory = $this->_resolve_directory($filename); + + return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE); + } + + /** + * Delete all cache entries. + * + * Beware of using this method when + * using shared memory cache systems, as it will wipe every + * entry within the system for all clients. + * + * // Delete all cache entries in the file group + * Cache::instance('file')->delete_all(); + * + * @return boolean + */ + public function delete_all() + { + return $this->_delete_file($this->_cache_dir, TRUE); + } + + /** + * Garbage collection method that cleans any expired + * cache entries from the cache. + * + * @return void + */ + public function garbage_collect() + { + $this->_delete_file($this->_cache_dir, TRUE, FALSE, TRUE); + return; + } + + /** + * Deletes files recursively and returns FALSE on any errors + * + * // Delete a file or folder whilst retaining parent directory and ignore all errors + * $this->_delete_file($folder, TRUE, TRUE); + * + * @param SplFileInfo $file file + * @param boolean $retain_parent_directory retain the parent directory + * @param boolean $ignore_errors ignore_errors to prevent all exceptions interrupting exec + * @param boolean $only_expired only expired files + * @return boolean + * @throws Cache_Exception + */ + protected function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE, $only_expired = FALSE) + { + // Allow graceful error handling + try + { + // If is file + if ($file->isFile()) + { + try + { + // Handle ignore files + if (in_array($file->getFilename(), $this->config('ignore_on_delete'))) + { + $delete = FALSE; + } + // If only expired is not set + elseif ($only_expired === FALSE) + { + // We want to delete the file + $delete = TRUE; + } + // Otherwise... + else + { + // Assess the file expiry to flag it for deletion + $json = $file->openFile('r')->current(); + $data = json_decode($json); + $delete = $data->expiry < time(); + } + + // If the delete flag is set delete file + if ($delete === TRUE) + return unlink($file->getRealPath()); + else + return FALSE; + } + catch (ErrorException $e) + { + // Catch any delete file warnings + if ($e->getCode() === E_WARNING) + { + throw new Cache_Exception(__METHOD__.' failed to delete file : :file', array(':file' => $file->getRealPath())); + } + } + } + // Else, is directory + elseif ($file->isDir()) + { + // Create new DirectoryIterator + $files = new DirectoryIterator($file->getPathname()); + + // Iterate over each entry + while ($files->valid()) + { + // Extract the entry name + $name = $files->getFilename(); + + // If the name is not a dot + if ($name != '.' AND $name != '..') + { + // Create new file resource + $fp = new SplFileInfo($files->getRealPath()); + // Delete the file + $this->_delete_file($fp); + } + + // Move the file pointer on + $files->next(); + } + + // If set to retain parent directory, return now + if ($retain_parent_directory) + { + return TRUE; + } + + try + { + // Remove the files iterator + // (fixes Windows PHP which has permission issues with open iterators) + unset($files); + + // Try to remove the parent directory + return rmdir($file->getRealPath()); + } + catch (ErrorException $e) + { + // Catch any delete directory warnings + if ($e->getCode() === E_WARNING) + { + throw new Cache_Exception(__METHOD__.' failed to delete directory : :directory', array(':directory' => $file->getRealPath())); + } + throw $e; + } + } + else + { + // We get here if a file has already been deleted + return FALSE; + } + } + // Catch all exceptions + catch (Exception $e) + { + // If ignore_errors is on + if ($ignore_errors === TRUE) + { + // Return + return FALSE; + } + // Throw exception + throw $e; + } + } + + /** + * Resolves the cache directory real path from the filename + * + * // Get the realpath of the cache folder + * $realpath = $this->_resolve_directory($filename); + * + * @param string $filename filename to resolve + * @return string + */ + protected function _resolve_directory($filename) + { + return $this->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.$filename[0].$filename[1].DIRECTORY_SEPARATOR; + } + + /** + * Makes the cache directory if it doesn't exist. Simply a wrapper for + * `mkdir` to ensure DRY principles + * + * @link http://php.net/manual/en/function.mkdir.php + * @param string $directory + * @param integer $mode + * @param boolean $recursive + * @param resource $context + * @return SplFileInfo + * @throws Cache_Exception + */ + protected function _make_directory($directory, $mode = 0777, $recursive = FALSE, $context = NULL) + { + if ( ! mkdir($directory, $mode, $recursive, $context)) + { + throw new Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory)); + } + chmod($directory, $mode); + + return new SplFileInfo($directory); + } +} diff --git a/modules/cache/classes/Kohana/Cache/GarbageCollect.php b/modules/cache/classes/Kohana/Cache/GarbageCollect.php new file mode 100644 index 0000000..c0bc519 --- /dev/null +++ b/modules/cache/classes/Kohana/Cache/GarbageCollect.php @@ -0,0 +1,23 @@ + array( // Default group + * 'driver' => 'memcache', // using Memcache driver + * 'servers' => array( // Available server definitions + * // First memcache server server + * array( + * 'host' => 'localhost', + * 'port' => 11211, + * 'persistent' => FALSE + * 'weight' => 1, + * 'timeout' => 1, + * 'retry_interval' => 15, + * 'status' => TRUE, + * 'instant_death' => TRUE, + * 'failure_callback' => array('className', 'classMethod') + * ), + * // Second memcache server + * array( + * 'host' => '192.168.1.5', + * 'port' => 22122, + * 'persistent' => TRUE + * ) + * ), + * 'compression' => FALSE, // Use compression? + * ), + * ) + * + * In cases where only one cache group is required, if the group is named `default` there is + * no need to pass the group name when instantiating a cache instance. + * + * #### General cache group configuration settings + * + * Below are the settings available to all types of cache driver. + * + * Name | Required | Description + * -------------- | -------- | --------------------------------------------------------------- + * driver | __YES__ | (_string_) The driver type to use + * servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below) + * compression | __NO__ | (_boolean_) Use data compression when caching + * + * #### Memcache server configuration + * + * The following settings should be used when defining each memcache server + * + * Name | Required | Description + * ---------------- | -------- | --------------------------------------------------------------- + * host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__ + * port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__ + * persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__ + * weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__ + * timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__ + * retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__ + * status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__ + * failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__ + * + * ### System requirements + * + * * Kohana 3.0.x + * * PHP 5.2.4 or greater + * * Memcache (plus Memcached-tags for native tagging support) + * * Zlib + * + * @package Kohana/Cache + * @category Base + * @version 2.0 + * @author Kohana Team + * @copyright (c) 2009-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_Cache_Memcache extends Cache implements Cache_Arithmetic { + + // Memcache has a maximum cache lifetime of 30 days + const CACHE_CEILING = 2592000; + + /** + * Memcache resource + * + * @var Memcache + */ + protected $_memcache; + + /** + * Flags to use when storing values + * + * @var string + */ + protected $_flags; + + /** + * The default configuration for the memcached server + * + * @var array + */ + protected $_default_config = array(); + + /** + * Constructs the memcache Kohana_Cache object + * + * @param array $config configuration + * @throws Cache_Exception + */ + protected function __construct(array $config) + { + // Check for the memcache extention + if ( ! extension_loaded('memcache')) + { + throw new Cache_Exception('Memcache PHP extention not loaded'); + } + + parent::__construct($config); + + // Setup Memcache + $this->_memcache = new Memcache; + + // Load servers from configuration + $servers = Arr::get($this->_config, 'servers', NULL); + + if ( ! $servers) + { + // Throw an exception if no server found + throw new Cache_Exception('No Memcache servers defined in configuration'); + } + + // Setup default server configuration + $this->_default_config = array( + 'host' => 'localhost', + 'port' => 11211, + 'persistent' => FALSE, + 'weight' => 1, + 'timeout' => 1, + 'retry_interval' => 15, + 'status' => TRUE, + 'instant_death' => TRUE, + 'failure_callback' => array($this, '_failed_request'), + ); + + // Add the memcache servers to the pool + foreach ($servers as $server) + { + // Merge the defined config with defaults + $server += $this->_default_config; + + if ( ! $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval'], $server['status'], $server['failure_callback'])) + { + throw new Cache_Exception('Memcache could not connect to host \':host\' using port \':port\'', array(':host' => $server['host'], ':port' => $server['port'])); + } + } + + // Setup the flags + $this->_flags = Arr::get($this->_config, 'compression', FALSE) ? MEMCACHE_COMPRESSED : FALSE; + } + + /** + * Retrieve a cached value entry by id. + * + * // Retrieve cache entry from memcache group + * $data = Cache::instance('memcache')->get('foo'); + * + * // Retrieve cache entry from memcache group and return 'bar' if miss + * $data = Cache::instance('memcache')->get('foo', 'bar'); + * + * @param string $id id of cache to entry + * @param string $default default value to return if cache miss + * @return mixed + * @throws Cache_Exception + */ + public function get($id, $default = NULL) + { + // Get the value from Memcache + $value = $this->_memcache->get($this->_sanitize_id($id)); + + // If the value wasn't found, normalise it + if ($value === FALSE) + { + $value = (NULL === $default) ? NULL : $default; + } + + // Return the value + return $value; + } + + /** + * Set a value to cache with id and lifetime + * + * $data = 'bar'; + * + * // Set 'bar' to 'foo' in memcache group for 10 minutes + * if (Cache::instance('memcache')->set('foo', $data, 600)) + * { + * // Cache was set successfully + * return + * } + * + * @param string $id id of cache entry + * @param mixed $data data to set to cache + * @param integer $lifetime lifetime in seconds, maximum value 2592000 + * @return boolean + */ + public function set($id, $data, $lifetime = 3600) + { + // If the lifetime is greater than the ceiling + if ($lifetime > Cache_Memcache::CACHE_CEILING) + { + // Set the lifetime to maximum cache time + $lifetime = Cache_Memcache::CACHE_CEILING + time(); + } + // Else if the lifetime is greater than zero + elseif ($lifetime > 0) + { + $lifetime += time(); + } + // Else + else + { + // Normalise the lifetime + $lifetime = 0; + } + + // Set the data to memcache + return $this->_memcache->set($this->_sanitize_id($id), $data, $this->_flags, $lifetime); + } + + /** + * Delete a cache entry based on id + * + * // Delete the 'foo' cache entry immediately + * Cache::instance('memcache')->delete('foo'); + * + * // Delete the 'bar' cache entry after 30 seconds + * Cache::instance('memcache')->delete('bar', 30); + * + * @param string $id id of entry to delete + * @param integer $timeout timeout of entry, if zero item is deleted immediately, otherwise the item will delete after the specified value in seconds + * @return boolean + */ + public function delete($id, $timeout = 0) + { + // Delete the id + return $this->_memcache->delete($this->_sanitize_id($id), $timeout); + } + + /** + * Delete all cache entries. + * + * Beware of using this method when + * using shared memory cache systems, as it will wipe every + * entry within the system for all clients. + * + * // Delete all cache entries in the default group + * Cache::instance('memcache')->delete_all(); + * + * @return boolean + */ + public function delete_all() + { + $result = $this->_memcache->flush(); + + // We must sleep after flushing, or overwriting will not work! + // @see http://php.net/manual/en/function.memcache-flush.php#81420 + sleep(1); + + return $result; + } + + /** + * Callback method for Memcache::failure_callback to use if any Memcache call + * on a particular server fails. This method switches off that instance of the + * server if the configuration setting `instant_death` is set to `TRUE`. + * + * @param string $hostname + * @param integer $port + * @return void|boolean + * @since 3.0.8 + */ + public function _failed_request($hostname, $port) + { + if ( ! $this->_config['instant_death']) + return; + + // Setup non-existent host + $host = FALSE; + + // Get host settings from configuration + foreach ($this->_config['servers'] as $server) + { + // Merge the defaults, since they won't always be set + $server += $this->_default_config; + // We're looking at the failed server + if ($hostname == $server['host'] and $port == $server['port']) + { + // Server to disable, since it failed + $host = $server; + continue; + } + } + + if ( ! $host) + return; + else + { + return $this->_memcache->setServerParams( + $host['host'], + $host['port'], + $host['timeout'], + $host['retry_interval'], + FALSE, // Server is offline + array($this, '_failed_request' + )); + } + } + + /** + * Increments a given value by the step value supplied. + * Useful for shared counters and other persistent integer based + * tracking. + * + * @param string id of cache entry to increment + * @param int step value to increment by + * @return integer + * @return boolean + */ + public function increment($id, $step = 1) + { + return $this->_memcache->increment($id, $step); + } + + /** + * Decrements a given value by the step value supplied. + * Useful for shared counters and other persistent integer based + * tracking. + * + * @param string id of cache entry to decrement + * @param int step value to decrement by + * @return integer + * @return boolean + */ + public function decrement($id, $step = 1) + { + return $this->_memcache->decrement($id, $step); + } +} \ No newline at end of file diff --git a/modules/cache/classes/Kohana/Cache/MemcacheTag.php b/modules/cache/classes/Kohana/Cache/MemcacheTag.php new file mode 100644 index 0000000..644e543 --- /dev/null +++ b/modules/cache/classes/Kohana/Cache/MemcacheTag.php @@ -0,0 +1,78 @@ +_memcache, 'tag_add')) + { + throw new Cache_Exception('Memcached-tags PHP plugin not present. Please see http://code.google.com/p/memcached-tags/ for more information'); + } + } + + /** + * Set a value based on an id with tags + * + * @param string $id id + * @param mixed $data data + * @param integer $lifetime lifetime [Optional] + * @param array $tags tags [Optional] + * @return boolean + */ + public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL) + { + $id = $this->_sanitize_id($id); + + $result = $this->set($id, $data, $lifetime); + + if ($result and $tags) + { + foreach ($tags as $tag) + { + $this->_memcache->tag_add($tag, $id); + } + } + + return $result; + } + + /** + * Delete cache entries based on a tag + * + * @param string $tag tag + * @return boolean + */ + public function delete_tag($tag) + { + return $this->_memcache->tag_delete($tag); + } + + /** + * Find cache entries based on a tag + * + * @param string $tag tag + * @return void + * @throws Cache_Exception + */ + public function find($tag) + { + throw new Cache_Exception('Memcached-tags does not support finding by tag'); + } +} diff --git a/modules/cache/classes/Kohana/Cache/Sqlite.php b/modules/cache/classes/Kohana/Cache/Sqlite.php new file mode 100644 index 0000000..932704a --- /dev/null +++ b/modules/cache/classes/Kohana/Cache/Sqlite.php @@ -0,0 +1,334 @@ +_config, 'database', NULL); + + if ($database === NULL) + { + throw new Cache_Exception('Database path not available in Kohana Cache configuration'); + } + + // Load new Sqlite DB + $this->_db = new PDO('sqlite:'.$database); + + // Test for existing DB + $result = $this->_db->query("SELECT * FROM sqlite_master WHERE name = 'caches' AND type = 'table'")->fetchAll(); + + // If there is no table, create a new one + if (0 == count($result)) + { + $database_schema = Arr::get($this->_config, 'schema', NULL); + + if ($database_schema === NULL) + { + throw new Cache_Exception('Database schema not found in Kohana Cache configuration'); + } + + try + { + // Create the caches table + $this->_db->query(Arr::get($this->_config, 'schema', NULL)); + } + catch (PDOException $e) + { + throw new Cache_Exception('Failed to create new SQLite caches table with the following error : :error', array(':error' => $e->getMessage())); + } + } + } + + /** + * Retrieve a value based on an id + * + * @param string $id id + * @param string $default default [Optional] Default value to return if id not found + * @return mixed + * @throws Cache_Exception + */ + public function get($id, $default = NULL) + { + // Prepare statement + $statement = $this->_db->prepare('SELECT id, expiration, cache FROM caches WHERE id = :id LIMIT 0, 1'); + + // Try and load the cache based on id + try + { + $statement->execute(array(':id' => $this->_sanitize_id($id))); + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + if ( ! $result = $statement->fetch(PDO::FETCH_OBJ)) + { + return $default; + } + + // If the cache has expired + if ($result->expiration != 0 and $result->expiration <= time()) + { + // Delete it and return default value + $this->delete($id); + return $default; + } + // Otherwise return cached object + else + { + // Disable notices for unserializing + $ER = error_reporting(~E_NOTICE); + + // Return the valid cache data + $data = unserialize($result->cache); + + // Turn notices back on + error_reporting($ER); + + // Return the resulting data + return $data; + } + } + + /** + * Set a value based on an id. Optionally add tags. + * + * @param string $id id + * @param mixed $data data + * @param integer $lifetime lifetime [Optional] + * @return boolean + */ + public function set($id, $data, $lifetime = NULL) + { + return (bool) $this->set_with_tags($id, $data, $lifetime); + } + + /** + * Delete a cache entry based on id + * + * @param string $id id + * @return boolean + * @throws Cache_Exception + */ + public function delete($id) + { + // Prepare statement + $statement = $this->_db->prepare('DELETE FROM caches WHERE id = :id'); + + // Remove the entry + try + { + $statement->execute(array(':id' => $this->_sanitize_id($id))); + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + return (bool) $statement->rowCount(); + } + + /** + * Delete all cache entries + * + * @return boolean + */ + public function delete_all() + { + // Prepare statement + $statement = $this->_db->prepare('DELETE FROM caches'); + + // Remove the entry + try + { + $statement->execute(); + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + return (bool) $statement->rowCount(); + } + + /** + * Set a value based on an id. Optionally add tags. + * + * @param string $id id + * @param mixed $data data + * @param integer $lifetime lifetime [Optional] + * @param array $tags tags [Optional] + * @return boolean + * @throws Cache_Exception + */ + public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL) + { + // Serialize the data + $data = serialize($data); + + // Normalise tags + $tags = (NULL === $tags) ? NULL : ('<'.implode('>,<', $tags).'>'); + + // Setup lifetime + if ($lifetime === NULL) + { + $lifetime = (0 === Arr::get($this->_config, 'default_expire', NULL)) ? 0 : (Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE) + time()); + } + else + { + $lifetime = (0 === $lifetime) ? 0 : ($lifetime + time()); + } + + // Prepare statement + // $this->exists() may throw Cache_Exception, no need to catch/rethrow + $statement = $this->exists($id) ? $this->_db->prepare('UPDATE caches SET expiration = :expiration, cache = :cache, tags = :tags WHERE id = :id') : $this->_db->prepare('INSERT INTO caches (id, cache, expiration, tags) VALUES (:id, :cache, :expiration, :tags)'); + + // Try to insert + try + { + $statement->execute(array(':id' => $this->_sanitize_id($id), ':cache' => $data, ':expiration' => $lifetime, ':tags' => $tags)); + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + return (bool) $statement->rowCount(); + } + + /** + * Delete cache entries based on a tag + * + * @param string $tag tag + * @return boolean + * @throws Cache_Exception + */ + public function delete_tag($tag) + { + // Prepare the statement + $statement = $this->_db->prepare('DELETE FROM caches WHERE tags LIKE :tag'); + + // Try to delete + try + { + $statement->execute(array(':tag' => "%<{$tag}>%")); + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + return (bool) $statement->rowCount(); + } + + /** + * Find cache entries based on a tag + * + * @param string $tag tag + * @return array + * @throws Cache_Exception + */ + public function find($tag) + { + // Prepare the statement + $statement = $this->_db->prepare('SELECT id, cache FROM caches WHERE tags LIKE :tag'); + + // Try to find + try + { + if ( ! $statement->execute(array(':tag' => "%<{$tag}>%"))) + { + return array(); + } + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + $result = array(); + + while ($row = $statement->fetchObject()) + { + // Disable notices for unserializing + $ER = error_reporting(~E_NOTICE); + + $result[$row->id] = unserialize($row->cache); + + // Turn notices back on + error_reporting($ER); + } + + return $result; + } + + /** + * Garbage collection method that cleans any expired + * cache entries from the cache. + * + * @return void + */ + public function garbage_collect() + { + // Create the sequel statement + $statement = $this->_db->prepare('DELETE FROM caches WHERE expiration < :expiration'); + + try + { + $statement->execute(array(':expiration' => time())); + } + catch (PDOException $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + } + + /** + * Tests whether an id exists or not + * + * @param string $id id + * @return boolean + * @throws Cache_Exception + */ + protected function exists($id) + { + $statement = $this->_db->prepare('SELECT id FROM caches WHERE id = :id'); + try + { + $statement->execute(array(':id' => $this->_sanitize_id($id))); + } + catch (PDOExeption $e) + { + throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); + } + + return (bool) $statement->fetchAll(); + } +} diff --git a/modules/cache/classes/Kohana/Cache/Tagging.php b/modules/cache/classes/Kohana/Cache/Tagging.php new file mode 100644 index 0000000..70d4d63 --- /dev/null +++ b/modules/cache/classes/Kohana/Cache/Tagging.php @@ -0,0 +1,41 @@ + array( // Driver group + * 'driver' => 'wincache', // using wincache driver + * ), + * ) + * + * In cases where only one cache group is required, if the group is named `default` there is + * no need to pass the group name when instantiating a cache instance. + * + * #### General cache group configuration settings + * + * Below are the settings available to all types of cache driver. + * + * Name | Required | Description + * -------------- | -------- | --------------------------------------------------------------- + * driver | __YES__ | (_string_) The driver type to use + * + * ### System requirements + * + * * Windows XP SP3 with IIS 5.1 and » FastCGI Extension + * * Windows Server 2003 with IIS 6.0 and » FastCGI Extension + * * Windows Vista SP1 with IIS 7.0 and FastCGI Module + * * Windows Server 2008 with IIS 7.0 and FastCGI Module + * * Windows 7 with IIS 7.5 and FastCGI Module + * * Windows Server 2008 R2 with IIS 7.5 and FastCGI Module + * * PHP 5.2.X, Non-thread-safe build + * * PHP 5.3 X86, Non-thread-safe VC9 build + * + * @package Kohana/Cache + * @category Base + * @author Kohana Team + * @copyright (c) 2009-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_Cache_Wincache extends Cache { + + /** + * Check for existence of the wincache extension This method cannot be invoked externally. The driver must + * be instantiated using the `Cache::instance()` method. + * + * @param array $config configuration + * @throws Cache_Exception + */ + protected function __construct(array $config) + { + if ( ! extension_loaded('wincache')) + { + throw new Cache_Exception('PHP wincache extension is not available.'); + } + + parent::__construct($config); + } + + /** + * Retrieve a cached value entry by id. + * + * // Retrieve cache entry from wincache group + * $data = Cache::instance('wincache')->get('foo'); + * + * // Retrieve cache entry from wincache group and return 'bar' if miss + * $data = Cache::instance('wincache')->get('foo', 'bar'); + * + * @param string $id id of cache to entry + * @param string $default default value to return if cache miss + * @return mixed + * @throws Cache_Exception + */ + public function get($id, $default = NULL) + { + $data = wincache_ucache_get($this->_sanitize_id($id), $success); + + return $success ? $data : $default; + } + + /** + * Set a value to cache with id and lifetime + * + * $data = 'bar'; + * + * // Set 'bar' to 'foo' in wincache group, using default expiry + * Cache::instance('wincache')->set('foo', $data); + * + * // Set 'bar' to 'foo' in wincache group for 30 seconds + * Cache::instance('wincache')->set('foo', $data, 30); + * + * @param string $id id of cache entry + * @param string $data data to set to cache + * @param integer $lifetime lifetime in seconds + * @return boolean + */ + public function set($id, $data, $lifetime = NULL) + { + if ($lifetime === NULL) + { + $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); + } + + return wincache_ucache_set($this->_sanitize_id($id), $data, $lifetime); + } + + /** + * Delete a cache entry based on id + * + * // Delete 'foo' entry from the wincache group + * Cache::instance('wincache')->delete('foo'); + * + * @param string $id id to remove from cache + * @return boolean + */ + public function delete($id) + { + return wincache_ucache_delete($this->_sanitize_id($id)); + } + + /** + * Delete all cache entries. + * + * Beware of using this method when + * using shared memory cache systems, as it will wipe every + * entry within the system for all clients. + * + * // Delete all cache entries in the wincache group + * Cache::instance('wincache')->delete_all(); + * + * @return boolean + */ + public function delete_all() + { + return wincache_ucache_clear(); + } +} diff --git a/modules/cache/classes/Kohana/HTTP/Cache.php b/modules/cache/classes/Kohana/HTTP/Cache.php new file mode 100644 index 0000000..2507f81 --- /dev/null +++ b/modules/cache/classes/Kohana/HTTP/Cache.php @@ -0,0 +1,503 @@ + FALSE + * ) + * ); + * + * // Create HTTP_Cache with supplied cache engine + * $http_cache = HTTP_Cache::factory(Cache::instance('memcache'), + * array( + * 'allow_private_cache' => FALSE + * ) + * ); + * + * @uses [Cache] + * @param mixed $cache cache engine to use + * @param array $options options to set to this class + * @return HTTP_Cache + */ + public static function factory($cache, array $options = array()) + { + if ( ! $cache instanceof Cache) + { + $cache = Cache::instance($cache); + } + + $options['cache'] = $cache; + + return new HTTP_Cache($options); + } + + /** + * Basic cache key generator that hashes the entire request and returns + * it. This is fine for static content, or dynamic content where user + * specific information is encoded into the request. + * + * // Generate cache key + * $cache_key = HTTP_Cache::basic_cache_key_generator($request); + * + * @param Request $request + * @return string + */ + public static function basic_cache_key_generator(Request $request) + { + $uri = $request->uri(); + $query = $request->query(); + $headers = $request->headers()->getArrayCopy(); + $body = $request->body(); + + return sha1($uri.'?'.http_build_query($query, NULL, '&').'~'.implode('~', $headers).'~'.$body); + } + + /** + * @var Cache cache driver to use for HTTP caching + */ + protected $_cache; + + /** + * @var callback Cache key generator callback + */ + protected $_cache_key_callback; + + /** + * @var boolean Defines whether this client should cache `private` cache directives + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + */ + protected $_allow_private_cache = FALSE; + + /** + * @var int The timestamp of the request + */ + protected $_request_time; + + /** + * @var int The timestamp of the response + */ + protected $_response_time; + + /** + * Constructor method for this class. Allows dependency injection of the + * required components such as `Cache` and the cache key generator. + * + * @param array $options + */ + public function __construct(array $options = array()) + { + foreach ($options as $key => $value) + { + if (method_exists($this, $key)) + { + $this->$key($value); + } + } + + if ($this->_cache_key_callback === NULL) + { + $this->cache_key_callback('HTTP_Cache::basic_cache_key_generator'); + } + } + + /** + * Executes the supplied [Request] with the supplied [Request_Client]. + * Before execution, the HTTP_Cache adapter checks the request type, + * destructive requests such as `POST`, `PUT` and `DELETE` will bypass + * cache completely and ensure the response is not cached. All other + * Request methods will allow caching, if the rules are met. + * + * @param Request_Client $client client to execute with Cache-Control + * @param Request $request request to execute with client + * @return [Response] + */ + public function execute(Request_Client $client, Request $request, Response $response) + { + if ( ! $this->_cache instanceof Cache) + return $client->execute_request($request, $response); + + // If this is a destructive request, by-pass cache completely + if (in_array($request->method(), array( + HTTP_Request::POST, + HTTP_Request::PUT, + HTTP_Request::DELETE))) + { + // Kill existing caches for this request + $this->invalidate_cache($request); + + $response = $client->execute_request($request, $response); + + $cache_control = HTTP_Header::create_cache_control(array( + 'no-cache', + 'must-revalidate' + )); + + // Ensure client respects destructive action + return $response->headers('cache-control', $cache_control); + } + + // Create the cache key + $cache_key = $this->create_cache_key($request, $this->_cache_key_callback); + + // Try and return cached version + if (($cached_response = $this->cache_response($cache_key, $request)) instanceof Response) + return $cached_response; + + // Start request time + $this->_request_time = time(); + + // Execute the request with the Request client + $response = $client->execute_request($request, $response); + + // Stop response time + $this->_response_time = (time() - $this->_request_time); + + // Cache the response + $this->cache_response($cache_key, $request, $response); + + $response->headers(HTTP_Cache::CACHE_STATUS_KEY, + HTTP_Cache::CACHE_STATUS_MISS); + + return $response; + } + + /** + * Invalidate a cached response for the [Request] supplied. + * This has the effect of deleting the response from the + * [Cache] entry. + * + * @param Request $request Response to remove from cache + * @return void + */ + public function invalidate_cache(Request $request) + { + if (($cache = $this->cache()) instanceof Cache) + { + $cache->delete($this->create_cache_key($request, $this->_cache_key_callback)); + } + + return; + } + + /** + * Getter and setter for the internal caching engine, + * used to cache responses if available and valid. + * + * @param Kohana_Cache $cache engine to use for caching + * @return Kohana_Cache + * @return Kohana_Request_Client + */ + public function cache(Cache $cache = NULL) + { + if ($cache === NULL) + return $this->_cache; + + $this->_cache = $cache; + return $this; + } + + /** + * Gets or sets the [Request_Client::allow_private_cache] setting. + * If set to `TRUE`, the client will also cache cache-control directives + * that have the `private` setting. + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + * @param boolean $setting allow caching of privately marked responses + * @return boolean + * @return [Request_Client] + */ + public function allow_private_cache($setting = NULL) + { + if ($setting === NULL) + return $this->_allow_private_cache; + + $this->_allow_private_cache = (bool) $setting; + return $this; + } + + /** + * Sets or gets the cache key generator callback for this caching + * class. The cache key generator provides a unique hash based on the + * `Request` object passed to it. + * + * The default generator is [HTTP_Cache::basic_cache_key_generator()], which + * serializes the entire `HTTP_Request` into a unique sha1 hash. This will + * provide basic caching for static and simple dynamic pages. More complex + * algorithms can be defined and then passed into `HTTP_Cache` using this + * method. + * + * // Get the cache key callback + * $callback = $http_cache->cache_key_callback(); + * + * // Set the cache key callback + * $http_cache->cache_key_callback('Foo::cache_key'); + * + * // Alternatively, in PHP 5.3 use a closure + * $http_cache->cache_key_callback(function (Request $request) { + * return sha1($request->render()); + * }); + * + * @param callback $callback + * @return mixed + * @throws HTTP_Exception + */ + public function cache_key_callback($callback = NULL) + { + if ($callback === NULL) + return $this->_cache_key_callback; + + if ( ! is_callable($callback)) + throw new Kohana_Exception('cache_key_callback must be callable!'); + + $this->_cache_key_callback = $callback; + return $this; + } + + /** + * Creates a cache key for the request to use for caching + * [Kohana_Response] returned by [Request::execute]. + * + * This is the default cache key generating logic, but can be overridden + * by setting [HTTP_Cache::cache_key_callback()]. + * + * @param Request $request request to create key for + * @param callback $callback optional callback to use instead of built-in method + * @return string + */ + public function create_cache_key(Request $request, $callback = FALSE) + { + if (is_callable($callback)) + return call_user_func($callback, $request); + else + return HTTP_Cache::basic_cache_key_generator($request); + } + + /** + * Controls whether the response can be cached. Uses HTTP + * protocol to determine whether the response can be cached. + * + * @link RFC 2616 http://www.w3.org/Protocols/rfc2616/ + * @param Response $response The Response + * @return boolean + */ + public function set_cache(Response $response) + { + $headers = $response->headers()->getArrayCopy(); + + if ($cache_control = Arr::get($headers, 'cache-control')) + { + // Parse the cache control + $cache_control = HTTP_Header::parse_cache_control($cache_control); + + // If the no-cache or no-store directive is set, return + if (array_intersect($cache_control, array('no-cache', 'no-store'))) + return FALSE; + + // Check for private cache and get out of here if invalid + if ( ! $this->_allow_private_cache AND in_array('private', $cache_control)) + { + if ( ! isset($cache_control['s-maxage'])) + return FALSE; + + // If there is a s-maxage directive we can use that + $cache_control['max-age'] = $cache_control['s-maxage']; + } + + // Check that max-age has been set and if it is valid for caching + if (isset($cache_control['max-age']) AND $cache_control['max-age'] < 1) + return FALSE; + } + + if ($expires = Arr::get($headers, 'expires') AND ! isset($cache_control['max-age'])) + { + // Can't cache things that have expired already + if (strtotime($expires) <= time()) + return FALSE; + } + + return TRUE; + } + + /** + * Caches a [Response] using the supplied [Cache] + * and the key generated by [Request_Client::_create_cache_key]. + * + * If not response is supplied, the cache will be checked for an existing + * one that is available. + * + * @param string $key the cache key to use + * @param Request $request the HTTP Request + * @param Response $response the HTTP Response + * @return mixed + */ + public function cache_response($key, Request $request, Response $response = NULL) + { + if ( ! $this->_cache instanceof Cache) + return FALSE; + + // Check for Pragma: no-cache + if ($pragma = $request->headers('pragma')) + { + if ($pragma == 'no-cache') + return FALSE; + elseif (is_array($pragma) AND in_array('no-cache', $pragma)) + return FALSE; + } + + // If there is no response, lookup an existing cached response + if ($response === NULL) + { + $response = $this->_cache->get($key); + + if ( ! $response instanceof Response) + return FALSE; + + // Do cache hit arithmetic, using fast arithmetic if available + if ($this->_cache instanceof Cache_Arithmetic) + { + $hit_count = $this->_cache->increment(HTTP_Cache::CACHE_HIT_KEY.$key); + } + else + { + $hit_count = $this->_cache->get(HTTP_Cache::CACHE_HIT_KEY.$key); + $this->_cache->set(HTTP_Cache::CACHE_HIT_KEY.$key, ++$hit_count); + } + + // Update the header to have correct HIT status and count + $response->headers(HTTP_Cache::CACHE_STATUS_KEY, + HTTP_Cache::CACHE_STATUS_HIT) + ->headers(HTTP_Cache::CACHE_HIT_KEY, $hit_count); + + return $response; + } + else + { + if (($ttl = $this->cache_lifetime($response)) === FALSE) + return FALSE; + + $response->headers(HTTP_Cache::CACHE_STATUS_KEY, + HTTP_Cache::CACHE_STATUS_SAVED); + + // Set the hit count to zero + $this->_cache->set(HTTP_Cache::CACHE_HIT_KEY.$key, 0); + + return $this->_cache->set($key, $response, $ttl); + } + } + + /** + * Calculates the total Time To Live based on the specification + * RFC 2616 cache lifetime rules. + * + * @param Response $response Response to evaluate + * @return mixed TTL value or false if the response should not be cached + */ + public function cache_lifetime(Response $response) + { + // Get out of here if this cannot be cached + if ( ! $this->set_cache($response)) + return FALSE; + + // Calculate apparent age + if ($date = $response->headers('date')) + { + $apparent_age = max(0, $this->_response_time - strtotime($date)); + } + else + { + $apparent_age = max(0, $this->_response_time); + } + + // Calculate corrected received age + if ($age = $response->headers('age')) + { + $corrected_received_age = max($apparent_age, intval($age)); + } + else + { + $corrected_received_age = $apparent_age; + } + + // Corrected initial age + $corrected_initial_age = $corrected_received_age + $this->request_execution_time(); + + // Resident time + $resident_time = time() - $this->_response_time; + + // Current age + $current_age = $corrected_initial_age + $resident_time; + + // Prepare the cache freshness lifetime + $ttl = NULL; + + // Cache control overrides + if ($cache_control = $response->headers('cache-control')) + { + // Parse the cache control header + $cache_control = HTTP_Header::parse_cache_control($cache_control); + + if (isset($cache_control['max-age'])) + { + $ttl = $cache_control['max-age']; + } + + if (isset($cache_control['s-maxage']) AND isset($cache_control['private']) AND $this->_allow_private_cache) + { + $ttl = $cache_control['s-maxage']; + } + + if (isset($cache_control['max-stale']) AND ! isset($cache_control['must-revalidate'])) + { + $ttl = $current_age + $cache_control['max-stale']; + } + } + + // If we have a TTL at this point, return + if ($ttl !== NULL) + return $ttl; + + if ($expires = $response->headers('expires')) + return strtotime($expires) - $current_age; + + return FALSE; + } + + /** + * Returns the duration of the last request execution. + * Either returns the time of completed requests or + * `FALSE` if the request hasn't finished executing, or + * is yet to be run. + * + * @return mixed + */ + public function request_execution_time() + { + if ($this->_request_time === NULL OR $this->_response_time === NULL) + return FALSE; + + return $this->_response_time - $this->_request_time; + } + +} // End Kohana_HTTP_Cache \ No newline at end of file diff --git a/modules/cache/config/cache.php b/modules/cache/config/cache.php new file mode 100644 index 0000000..acc567e --- /dev/null +++ b/modules/cache/config/cache.php @@ -0,0 +1,70 @@ + array( + 'driver' => 'memcache', + 'default_expire' => 3600, + 'compression' => FALSE, // Use Zlib compression (can cause issues with integers) + 'servers' => array( + 'local' => array( + 'host' => 'localhost', // Memcache Server + 'port' => 11211, // Memcache port number + 'persistent' => FALSE, // Persistent connection + 'weight' => 1, + 'timeout' => 1, + 'retry_interval' => 15, + 'status' => TRUE, + ), + ), + 'instant_death' => TRUE, // Take server offline immediately on first fail (no retry) + ), + 'memcachetag' => array( + 'driver' => 'memcachetag', + 'default_expire' => 3600, + 'compression' => FALSE, // Use Zlib compression (can cause issues with integers) + 'servers' => array( + 'local' => array( + 'host' => 'localhost', // Memcache Server + 'port' => 11211, // Memcache port number + 'persistent' => FALSE, // Persistent connection + 'weight' => 1, + 'timeout' => 1, + 'retry_interval' => 15, + 'status' => TRUE, + ), + ), + 'instant_death' => TRUE, + ), + 'apc' => array( + 'driver' => 'apc', + 'default_expire' => 3600, + ), + 'wincache' => array( + 'driver' => 'wincache', + 'default_expire' => 3600, + ), + 'sqlite' => array( + 'driver' => 'sqlite', + 'default_expire' => 3600, + 'database' => APPPATH.'cache/kohana-cache.sql3', + 'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, tags VARCHAR(255), expiration INTEGER, cache TEXT)', + ), + 'eaccelerator' => array( + 'driver' => 'eaccelerator', + ), + 'xcache' => array( + 'driver' => 'xcache', + 'default_expire' => 3600, + ), + 'file' => array( + 'driver' => 'file', + 'cache_dir' => APPPATH.'cache', + 'default_expire' => 3600, + 'ignore_on_delete' => array( + '.gitignore', + '.git', + '.svn' + ) + ) +*/ +); diff --git a/modules/cache/config/userguide.php b/modules/cache/config/userguide.php new file mode 100644 index 0000000..0756600 --- /dev/null +++ b/modules/cache/config/userguide.php @@ -0,0 +1,23 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'cache' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'Cache', + + // A short description of this module, shown on the index page + 'description' => 'Common interface for caching engines.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2012 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/modules/cache/guide/cache.usage.md b/modules/cache/guide/cache.usage.md new file mode 100644 index 0000000..15d7c52 --- /dev/null +++ b/modules/cache/guide/cache.usage.md @@ -0,0 +1,219 @@ +# Kohana Cache usage + +[Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers. + +## Getting a new cache instance + +Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor. + + // Create a new instance of cache using the default group + $cache = Cache::instance(); + +The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group. + +To create a cache instance using a group other than the _default_, simply provide the group name as an argument. + + // Create a new instance of the memcache group + $memcache = Cache::instance('memcache'); + +If there is a cache instance already instantiated then you can get it directly from the class member. + + [!!] Beware that this can cause issues if you do not test for the instance before trying to access it. + + // Check for the existance of the cache driver + if (isset(Cache::$instances['memcache'])) + { + // Get the existing cache instance directly (faster) + $memcache = Cache::$instances['memcache']; + } + else + { + // Get the cache driver instance (slower) + $memcache = Cache::instance('memcache'); + } + +## Setting and getting variables to and from cache + +The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification. + + [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources. + +### Setting a value to cache + +Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations. + +The first example demonstrates how to quickly load and set a value to the default cache instance. + + // Create a cachable object + $object = new stdClass; + + // Set a property + $object->foo = 'bar'; + + // Cache the object using default group (quick interface) with default time (3600 seconds) + Cache::instance()->set('foo', $object); + +If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below. + + // Set the object using a defined group for a defined time period (30 seconds) + $memcache = Cache::instance('memcache'); + $memcache->set('foo', $object, 30); + +#### Setting a value with tags + +Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface. + + // Get a cache instance that supports tags + $memcache = Cache::instance('memcachetag'); + + // Test for tagging interface + if ($memcache instanceof Cache_Tagging) + { + // Set a value with some tags for 30 seconds + $memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar')); + } + // Otherwise set without tags + else + { + // Set a value for 30 seconds + $memcache->set('foo', $object, 30); + } + +It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard. + +### Getting a value from cache + +Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry. + + // Retrieve a value from cache (quickly) + $object = Cache::instance()->get('foo'); + +In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested. + + // If the cache key is available (with default value set to FALSE) + if ($object = Cache::instance()->get('foo', FALSE)) + { + // Do something + } + else + { + // Do something else + } + +#### Getting values from cache using tags + +It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging. + + [!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception. + + // Get an instance of cache + $cache = Cache::instance('memcachetag'); + + // Wrap in a try/catch statement to gracefully handle memcachetag + try + { + // Find values based on tag + return $cache->find('snafu'); + } + catch (Cache_Exception $e) + { + // Handle gracefully + return FALSE; + } + +### Deleting values from cache + +Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories: + + - __Delete value by key__. Deletes a cached value by the associated key. + - __Delete all values__. Deletes all caches values stored in the cache instance. + - __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite. + +#### Delete value by key + +To delete a specific value by its associated key: + + // If the cache entry for 'foo' is deleted + if (Cache::instance()->delete('foo')) + { + // Cache entry successfully deleted, do something + } + +By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache. + +#### Delete all values + +To delete all values in a specific instance: + + // If all cache items where deleted successfully + if (Cache::instance()->delete_all()) + { + // Do something + } + +It is also possible to delete all cache items in every instance: + + // For each cache instance + foreach (Cache::$instances as $group => $instance) + { + if ($instance->delete_all()) + { + var_dump('instance : '.$group.' has been flushed!'); + } + } + +#### Delete values by tag + +Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag. + + // Get cache instance + $cache = Cache::instance(); + + // Check for tagging interface + if ($cache instanceof Cache_Tagging) + { + // Delete all entries by the tag 'snafu' + $cache->delete_tag('snafu'); + } + +#### Garbage Collection + +Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable. + +When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below. + + // Get a cache instance + $cache_file = Cache::instance('file'); + + // Set a GC probability of 10% + $gc = 10; + + // If the GC probability is a hit + if (rand(0,99) <= $gc and $cache_file instanceof Cache_GarbageCollect) + { + // Garbage Collect + $cache_file->garbage_collect(); + } + +# Interfaces + +Kohana Cache comes with two interfaces that are implemented where the drivers support them: + + - __[Cache_Tagging] for tagging support on cache entries__ + - [Cache_MemcacheTag] + - [Cache_Sqlite] + - __[Cache_GarbageCollect] for garbage collection with drivers without native support__ + - [Cache_File] + - [Cache_Sqlite] + +When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method. + + // Create a cache instance + $cache = Cache::instance(); + + // Test for Garbage Collection + if ($cache instanceof Cache_GarbageCollect) + { + // Collect garbage + $cache->garbage_collect(); + } \ No newline at end of file diff --git a/modules/cache/guide/cache/config.md b/modules/cache/guide/cache/config.md new file mode 100644 index 0000000..450bea8 --- /dev/null +++ b/modules/cache/guide/cache/config.md @@ -0,0 +1,162 @@ +# Kohana Cache configuration + +Kohana Cache uses configuration groups to create cache instances. A configuration group can +use any supported driver, with successive groups using multiple instances of the same driver type. + +The default cache group is loaded based on the `Cache::$default` setting. It is set to the `file` driver as standard, however this can be changed within the `/application/boostrap.php` file. + + // Change the default cache driver to memcache + Cache::$default = 'memcache'; + + // Load the memcache cache driver using default setting + $memcache = Cache::instance(); + +## Group settings + +Below are the default cache configuration groups for each supported driver. Add to- or override these settings +within the `application/config/cache.php` file. + +Name | Required | Description +-------------- | -------- | --------------------------------------------------------------- +driver | __YES__ | (_string_) The driver type to use +default_expire | __NO__ | (_string_) The driver type to use + + + 'file' => array + ( + 'driver' => 'file', + 'cache_dir' => APPPATH.'cache/.kohana_cache', + 'default_expire' => 3600, + ), + +## Memcache & Memcached-tag settings + +Name | Required | Description +-------------- | -------- | --------------------------------------------------------------- +driver | __YES__ | (_string_) The driver type to use +servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below) +compression | __NO__ | (_boolean_) Use data compression when caching + +### Memcache server configuration + +Name | Required | Description +---------------- | -------- | --------------------------------------------------------------- +host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__ +port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__ +persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__ +weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__ +timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__ +retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__ +status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__ +failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__ + + 'memcache' => array + ( + 'driver' => 'memcache', + 'default_expire' => 3600, + 'compression' => FALSE, // Use Zlib compression + (can cause issues with integers) + 'servers' => array + ( + 'local' => array + ( + 'host' => 'localhost', // Memcache Server + 'port' => 11211, // Memcache port number + 'persistent' => FALSE, // Persistent connection + ), + ), + ), + 'memcachetag' => array + ( + 'driver' => 'memcachetag', + 'default_expire' => 3600, + 'compression' => FALSE, // Use Zlib compression + (can cause issues with integers) + 'servers' => array + ( + 'local' => array + ( + 'host' => 'localhost', // Memcache Server + 'port' => 11211, // Memcache port number + 'persistent' => FALSE, // Persistent connection + ), + ), + ), + +## APC settings + + 'apc' => array + ( + 'driver' => 'apc', + 'default_expire' => 3600, + ), + +## SQLite settings + + 'sqlite' => array + ( + 'driver' => 'sqlite', + 'default_expire' => 3600, + 'database' => APPPATH.'cache/kohana-cache.sql3', + 'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, + tags VARCHAR(255), expiration INTEGER, cache TEXT)', + ), + +## File settings + + 'file' => array + ( + 'driver' => 'file', + 'cache_dir' => 'cache/.kohana_cache', + 'default_expire' => 3600, + ) + +## Wincache settings + + 'wincache' => array + ( + 'driver' => 'wincache', + 'default_expire' => 3600, + ), + + +## Override existing configuration group + +The following example demonstrates how to override an existing configuration setting, using the config file in `/application/config/cache.php`. + + array + ( + 'driver' => 'memcache', // Use Memcached as the default driver + 'default_expire' => 8000, // Overide default expiry + 'servers' => array + ( + // Add a new server + array + ( + 'host' => 'cache.domain.tld', + 'port' => 11211, + 'persistent' => FALSE + ) + ), + 'compression' => FALSE + ) + ); + +## Add new configuration group + +The following example demonstrates how to add a new configuration setting, using the config file in `/application/config/cache.php`. + + array + ( + 'driver' => 'apc', // Use Memcached as the default driver + 'default_expire' => 1000, // Overide default expiry + ) + ); diff --git a/modules/cache/guide/cache/examples.md b/modules/cache/guide/cache/examples.md new file mode 100644 index 0000000..e69de29 diff --git a/modules/cache/guide/cache/index.md b/modules/cache/guide/cache/index.md new file mode 100644 index 0000000..0df10c9 --- /dev/null +++ b/modules/cache/guide/cache/index.md @@ -0,0 +1,57 @@ +# About Kohana Cache + +[Kohana_Cache] provides a common interface to a variety of caching engines. [Cache_Tagging] is +supported where available natively to the cache system. Kohana Cache supports multiple +instances of cache engines through a grouped singleton pattern. + +## Supported cache engines + + * APC ([Cache_Apc]) + * File ([Cache_File]) + * Memcached ([Cache_Memcache]) + * Memcached-tags ([Cache_Memcachetag]) + * SQLite ([Cache_Sqlite]) + * Wincache + +## Introduction to caching + +Caching should be implemented with consideration. Generally, caching the result of resources +is faster than reprocessing them. Choosing what, how and when to cache is vital. [PHP APC](http://php.net/manual/en/book.apc.php) is one of the fastest caching systems available, closely followed by [Memcached](http://memcached.org/). [SQLite](http://www.sqlite.org/) and File caching are two of the slowest cache methods, however usually faster than reprocessing +a complex set of instructions. + +Caching engines that use memory are considerably faster than file based alternatives. But +memory is limited whereas disk space is plentiful. If caching large datasets, such as large database result sets, it is best to use file caching. + + [!!] Cache drivers require the relevant PHP extensions to be installed. APC, eAccelerator, Memecached and Xcache all require non-standard PHP extensions. + +## What the Kohana Cache module does (and does not do) + +This module provides a simple abstracted interface to a wide selection of popular PHP cache engines. The caching API provides the basic caching methods implemented across all solutions, memory, network or disk based. Basic key / value storing is supported by all drivers, with additional tagging and garbage collection support where implemented or required. + +_Kohana Cache_ does not provide HTTP style caching for clients (web browsers) and/or proxies (_Varnish_, _Squid_). There are other Kohana modules that provide this functionality. + +## Choosing a cache provider + +Getting and setting values to cache is very simple when using the _Kohana Cache_ interface. The hardest choice is choosing which cache engine to use. When choosing a caching engine, the following criteria must be considered: + + 1. __Does the cache need to be distributed?__ + This is an important consideration as it will severely limit the options available to solutions such as Memcache when a distributed solution is required. + 2. __Does the cache need to be fast?__ + In almost all cases retrieving data from a cache is faster than execution. However generally memory based caching is considerably faster than disk based caching (see table below). + 3. __How much cache is required?__ + Cache is not endless, and memory based caches are subject to a considerably more limited storage resource. + +Driver | Storage | Speed | Tags | Distributed | Automatic Garbage Collection | Notes +---------------- | ------------ | --------- | -------- | ----------- | ---------------------------- | ----------------------- +APC | __Memory__ | Excellent | No | No | Yes | Widely available PHP opcode caching solution, improves php execution performance +Wincache | __Memory__ | Excellent | No | No | Yes | Windows variant of APC +File | __Disk__ | Poor | No | No | No | Marginally faster than execution +Memcache (tag) | __Memory__ | Good | No (yes) | Yes | Yes | Generally fast distributed solution, but has a speed hit due to variable network latency and serialization +Sqlite | __Disk__ | Poor | Yes | No | No | Marginally faster than execution + +It is possible to have hybrid cache solutions that use a combination of the engines above in different contexts. This is supported with _Kohana Cache_ as well + +## Minimum requirements + + * Kohana 3.0.4 + * PHP 5.2.4 or greater \ No newline at end of file diff --git a/modules/cache/guide/cache/menu.md b/modules/cache/guide/cache/menu.md new file mode 100644 index 0000000..5218558 --- /dev/null +++ b/modules/cache/guide/cache/menu.md @@ -0,0 +1,3 @@ +## [Cache]() +- [Configuration](config) +- [Usage](usage) \ No newline at end of file diff --git a/modules/cache/guide/cache/usage.md b/modules/cache/guide/cache/usage.md new file mode 100644 index 0000000..15d7c52 --- /dev/null +++ b/modules/cache/guide/cache/usage.md @@ -0,0 +1,219 @@ +# Kohana Cache usage + +[Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers. + +## Getting a new cache instance + +Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor. + + // Create a new instance of cache using the default group + $cache = Cache::instance(); + +The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group. + +To create a cache instance using a group other than the _default_, simply provide the group name as an argument. + + // Create a new instance of the memcache group + $memcache = Cache::instance('memcache'); + +If there is a cache instance already instantiated then you can get it directly from the class member. + + [!!] Beware that this can cause issues if you do not test for the instance before trying to access it. + + // Check for the existance of the cache driver + if (isset(Cache::$instances['memcache'])) + { + // Get the existing cache instance directly (faster) + $memcache = Cache::$instances['memcache']; + } + else + { + // Get the cache driver instance (slower) + $memcache = Cache::instance('memcache'); + } + +## Setting and getting variables to and from cache + +The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification. + + [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources. + +### Setting a value to cache + +Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations. + +The first example demonstrates how to quickly load and set a value to the default cache instance. + + // Create a cachable object + $object = new stdClass; + + // Set a property + $object->foo = 'bar'; + + // Cache the object using default group (quick interface) with default time (3600 seconds) + Cache::instance()->set('foo', $object); + +If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below. + + // Set the object using a defined group for a defined time period (30 seconds) + $memcache = Cache::instance('memcache'); + $memcache->set('foo', $object, 30); + +#### Setting a value with tags + +Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface. + + // Get a cache instance that supports tags + $memcache = Cache::instance('memcachetag'); + + // Test for tagging interface + if ($memcache instanceof Cache_Tagging) + { + // Set a value with some tags for 30 seconds + $memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar')); + } + // Otherwise set without tags + else + { + // Set a value for 30 seconds + $memcache->set('foo', $object, 30); + } + +It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard. + +### Getting a value from cache + +Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry. + + // Retrieve a value from cache (quickly) + $object = Cache::instance()->get('foo'); + +In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested. + + // If the cache key is available (with default value set to FALSE) + if ($object = Cache::instance()->get('foo', FALSE)) + { + // Do something + } + else + { + // Do something else + } + +#### Getting values from cache using tags + +It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging. + + [!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception. + + // Get an instance of cache + $cache = Cache::instance('memcachetag'); + + // Wrap in a try/catch statement to gracefully handle memcachetag + try + { + // Find values based on tag + return $cache->find('snafu'); + } + catch (Cache_Exception $e) + { + // Handle gracefully + return FALSE; + } + +### Deleting values from cache + +Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories: + + - __Delete value by key__. Deletes a cached value by the associated key. + - __Delete all values__. Deletes all caches values stored in the cache instance. + - __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite. + +#### Delete value by key + +To delete a specific value by its associated key: + + // If the cache entry for 'foo' is deleted + if (Cache::instance()->delete('foo')) + { + // Cache entry successfully deleted, do something + } + +By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache. + +#### Delete all values + +To delete all values in a specific instance: + + // If all cache items where deleted successfully + if (Cache::instance()->delete_all()) + { + // Do something + } + +It is also possible to delete all cache items in every instance: + + // For each cache instance + foreach (Cache::$instances as $group => $instance) + { + if ($instance->delete_all()) + { + var_dump('instance : '.$group.' has been flushed!'); + } + } + +#### Delete values by tag + +Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag. + + // Get cache instance + $cache = Cache::instance(); + + // Check for tagging interface + if ($cache instanceof Cache_Tagging) + { + // Delete all entries by the tag 'snafu' + $cache->delete_tag('snafu'); + } + +#### Garbage Collection + +Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable. + +When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below. + + // Get a cache instance + $cache_file = Cache::instance('file'); + + // Set a GC probability of 10% + $gc = 10; + + // If the GC probability is a hit + if (rand(0,99) <= $gc and $cache_file instanceof Cache_GarbageCollect) + { + // Garbage Collect + $cache_file->garbage_collect(); + } + +# Interfaces + +Kohana Cache comes with two interfaces that are implemented where the drivers support them: + + - __[Cache_Tagging] for tagging support on cache entries__ + - [Cache_MemcacheTag] + - [Cache_Sqlite] + - __[Cache_GarbageCollect] for garbage collection with drivers without native support__ + - [Cache_File] + - [Cache_Sqlite] + +When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method. + + // Create a cache instance + $cache = Cache::instance(); + + // Test for Garbage Collection + if ($cache instanceof Cache_GarbageCollect) + { + // Collect garbage + $cache->garbage_collect(); + } \ No newline at end of file diff --git a/modules/cache/tests/cache/CacheBasicMethodsTest.php b/modules/cache/tests/cache/CacheBasicMethodsTest.php new file mode 100644 index 0000000..5fdae60 --- /dev/null +++ b/modules/cache/tests/cache/CacheBasicMethodsTest.php @@ -0,0 +1,299 @@ +_cache_driver; + + $this->_cache_driver = $cache; + return $this; + } + + /** + * Data provider for test_set_get() + * + * @return array + */ + public function provider_set_get() + { + $object = new StdClass; + $object->foo = 'foo'; + $object->bar = 'bar'; + + $html_text = << + + + + + + +TESTTEXT; + + return array( + array( + array( + 'id' => 'string', // Key to set to cache + 'value' => 'foobar', // Value to set to key + 'ttl' => 0, // Time to live + 'wait' => FALSE, // Test wait time to let cache expire + 'type' => 'string', // Type test + 'default' => NULL // Default value get should return + ), + 'foobar' + ), + array( + array( + 'id' => 'integer', + 'value' => 101010, + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'integer', + 'default' => NULL + ), + 101010 + ), + array( + array( + 'id' => 'float', + 'value' => 10.00, + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'float', + 'default' => NULL + ), + 10.00 + ), + array( + array( + 'id' => 'array', + 'value' => array( + 'key' => 'foo', + 'value' => 'bar' + ), + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'array', + 'default' => NULL + ), + array( + 'key' => 'foo', + 'value' => 'bar' + ) + ), + array( + array( + 'id' => 'boolean', + 'value' => TRUE, + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'boolean', + 'default' => NULL + ), + TRUE + ), + array( + array( + 'id' => 'null', + 'value' => NULL, + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'null', + 'default' => NULL + ), + NULL + ), + array( + array( + 'id' => 'object', + 'value' => $object, + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'object', + 'default' => NULL + ), + $object + ), + array( + array( + 'id' => 'bar\\ with / troublesome key', + 'value' => 'foo bar snafu', + 'ttl' => 0, + 'wait' => FALSE, + 'type' => 'string', + 'default' => NULL + ), + 'foo bar snafu' + ), + array( + array( + 'id' => 'bar', + 'value' => 'foo', + 'ttl' => 3, + 'wait' => 5, + 'type' => 'null', + 'default' => NULL + ), + NULL + ), + array( + array( + 'id' => 'snafu', + 'value' => 'fubar', + 'ttl' => 3, + 'wait' => 5, + 'type' => 'string', + 'default' => 'something completely different!' + ), + 'something completely different!' + ), + array( + array( + 'id' => 'new line test with HTML', + 'value' => $html_text, + 'ttl' => 10, + 'wait' => FALSE, + 'type' => 'string', + 'default' => NULL, + ), + $html_text + ) + ); + } + + /** + * Tests the [Cache::set()] method, testing; + * + * - The value is cached + * - The lifetime is respected + * - The returned value type is as expected + * - The default not-found value is respected + * + * @dataProvider provider_set_get + * + * @param array data + * @param mixed expected + * @return void + */ + public function test_set_get(array $data, $expected) + { + $cache = $this->cache(); + extract($data); + + $this->assertTrue($cache->set($id, $value, $ttl)); + + if ($wait !== FALSE) + { + // Lets let the cache expire + sleep($wait); + } + + $result = $cache->get($id, $default); + $this->assertEquals($expected, $result); + $this->assertInternalType($type, $result); + + unset($id, $value, $ttl, $wait, $type, $default); + } + + /** + * Tests the [Cache::delete()] method, testing; + * + * - The a cached value is deleted from cache + * - The cache returns a TRUE value upon deletion + * - The cache returns a FALSE value if no value exists to delete + * + * @return void + */ + public function test_delete() + { + // Init + $cache = $this->cache(); + $cache->delete_all(); + + // Test deletion if real cached value + if ( ! $cache->set('test_delete_1', 'This should not be here!', 0)) + { + $this->fail('Unable to set cache value to delete!'); + } + + // Test delete returns TRUE and check the value is gone + $this->assertTrue($cache->delete('test_delete_1')); + $this->assertNull($cache->get('test_delete_1')); + + // Test non-existant cache value returns FALSE if no error + $this->assertFalse($cache->delete('test_delete_1')); + } + + /** + * Tests [Cache::delete_all()] works as specified + * + * @return void + * @uses Kohana_CacheBasicMethodsTest::provider_set_get() + */ + public function test_delete_all() + { + // Init + $cache = $this->cache(); + $data = $this->provider_set_get(); + + foreach ($data as $key => $values) + { + extract($values[0]); + if ( ! $cache->set($id, $value)) + { + $this->fail('Unable to set: '.$key.' => '.$value.' to cache'); + } + unset($id, $value, $ttl, $wait, $type, $default); + } + + // Test delete_all is successful + $this->assertTrue($cache->delete_all()); + + foreach ($data as $key => $values) + { + // Verify data has been purged + $this->assertSame('Cache Deleted!', $cache->get($values[0]['id'], + 'Cache Deleted!')); + } + } + +} // End Kohana_CacheBasicMethodsTest diff --git a/modules/cache/tests/cache/CacheTest.php b/modules/cache/tests/cache/CacheTest.php new file mode 100644 index 0000000..a5c7564 --- /dev/null +++ b/modules/cache/tests/cache/CacheTest.php @@ -0,0 +1,242 @@ +load('cache.file')) + { + $base = array( + // Test default group + array( + NULL, + Cache::instance('file') + ), + // Test defined group + array( + 'file', + Cache::instance('file') + ), + ); + } + + + return array( + // Test bad group definition + $base+array( + Kohana_CacheTest::BAD_GROUP_DEFINITION, + 'Failed to load Kohana Cache group: 1010' + ), + ); + } + + /** + * Tests the [Cache::factory()] method behaves as expected + * + * @dataProvider provider_instance + * + * @return void + */ + public function test_instance($group, $expected) + { + if (in_array($group, array( + Kohana_CacheTest::BAD_GROUP_DEFINITION, + ) + )) + { + $this->setExpectedException('Cache_Exception'); + } + + try + { + $cache = Cache::instance($group); + } + catch (Cache_Exception $e) + { + $this->assertSame($expected, $e->getMessage()); + throw $e; + } + + $this->assertInstanceOf(get_class($expected), $cache); + $this->assertSame($expected->config(), $cache->config()); + } + + /** + * Tests that `clone($cache)` will be prevented to maintain singleton + * + * @return void + * @expectedException Cache_Exception + */ + public function test_cloning_fails() + { + if ( ! Kohana::$config->load('cache.file')) + { + $this->markTestSkipped('Unable to load File configuration'); + } + + try + { + $cache_clone = clone(Cache::instance('file')); + } + catch (Cache_Exception $e) + { + $this->assertSame('Cloning of Kohana_Cache objects is forbidden', + $e->getMessage()); + throw $e; + } + } + + /** + * Data provider for test_config + * + * @return array + */ + public function provider_config() + { + return array( + array( + array( + 'server' => 'otherhost', + 'port' => 5555, + 'persistent' => TRUE, + ), + NULL, + Kohana_CacheTest::EXPECT_SELF, + array( + 'server' => 'otherhost', + 'port' => 5555, + 'persistent' => TRUE, + ), + ), + array( + 'foo', + 'bar', + Kohana_CacheTest::EXPECT_SELF, + array( + 'foo' => 'bar' + ) + ), + array( + 'server', + NULL, + NULL, + array() + ), + array( + NULL, + NULL, + array(), + array() + ) + ); + } + + /** + * Tests the config method behaviour + * + * @dataProvider provider_config + * + * @param mixed key value to set or get + * @param mixed value to set to key + * @param mixed expected result from [Cache::config()] + * @param array expected config within cache + * @return void + */ + public function test_config($key, $value, $expected_result, array $expected_config) + { + $cache = $this->getMock('Cache_File', NULL, array(), '', FALSE); + + if ($expected_result === Kohana_CacheTest::EXPECT_SELF) + { + $expected_result = $cache; + } + + $this->assertSame($expected_result, $cache->config($key, $value)); + $this->assertSame($expected_config, $cache->config()); + } + + /** + * Data provider for test_sanitize_id + * + * @return array + */ + public function provider_sanitize_id() + { + return array( + array( + 'foo', + 'foo' + ), + array( + 'foo+-!@', + 'foo+-!@' + ), + array( + 'foo/bar', + 'foo_bar', + ), + array( + 'foo\\bar', + 'foo_bar' + ), + array( + 'foo bar', + 'foo_bar' + ), + array( + 'foo\\bar snafu/stfu', + 'foo_bar_snafu_stfu' + ) + ); + } + + /** + * Tests the [Cache::_sanitize_id()] method works as expected. + * This uses some nasty reflection techniques to access a protected + * method. + * + * @dataProvider provider_sanitize_id + * + * @param string id + * @param string expected + * @return void + */ + public function test_sanitize_id($id, $expected) + { + $cache = $this->getMock('Cache', array( + 'get', + 'set', + 'delete', + 'delete_all' + ), array(array()), + '', FALSE + ); + + $cache_reflection = new ReflectionClass($cache); + $sanitize_id = $cache_reflection->getMethod('_sanitize_id'); + $sanitize_id->setAccessible(TRUE); + + $this->assertSame($expected, $sanitize_id->invoke($cache, $id)); + } +} // End Kohana_CacheTest diff --git a/modules/cache/tests/cache/FileTest.php b/modules/cache/tests/cache/FileTest.php new file mode 100644 index 0000000..803258f --- /dev/null +++ b/modules/cache/tests/cache/FileTest.php @@ -0,0 +1,98 @@ +load('cache.file')) + { + $this->markTestSkipped('Unable to load File configuration'); + } + + $this->cache(Cache::instance('file')); + } + + /** + * Tests that ignored files are not removed from file cache + * + * @return void + */ + public function test_ignore_delete_file() + { + $cache = $this->cache(); + $config = Kohana::$config->load('cache')->file; + $file = $config['cache_dir'].'/.gitignore'; + + // Lets pollute the cache folder + file_put_contents($file, 'foobar'); + + $this->assertTrue($cache->delete_all()); + $this->assertTrue(file_exists($file)); + $this->assertEquals('foobar', file_get_contents($file)); + + unlink($file); + } + + /** + * Provider for test_utf8 + * + * @return array + */ + public function provider_utf8() + { + return array( + array( + 'This is â ütf-8 Ӝ☃ string', + 'This is â ütf-8 Ӝ☃ string' + ), + array( + '㆓㆕㆙㆛', + '㆓㆕㆙㆛' + ), + array( + 'அஆஇஈஊ', + 'அஆஇஈஊ' + ) + ); + } + + /** + * Tests the file driver supports utf-8 strings + * + * @dataProvider provider_utf8 + * + * @return void + */ + public function test_utf8($input, $expected) + { + $cache = $this->cache(); + $cache->set('utf8', $input); + + $this->assertSame($expected, $cache->get('utf8')); + } + +} // End Kohana_SqliteTest diff --git a/modules/cache/tests/cache/SqliteTest.php b/modules/cache/tests/cache/SqliteTest.php new file mode 100644 index 0000000..4a9c2ea --- /dev/null +++ b/modules/cache/tests/cache/SqliteTest.php @@ -0,0 +1,44 @@ +markTestSkipped('SQLite PDO PHP Extension is not available'); + } + + if ( ! Kohana::$config->load('cache.sqlite')) + { + $this->markTestIncomplete('Unable to load sqlite configuration'); + } + + $this->cache(Cache::instance('sqlite')); + } + +} // End Kohana_SqliteTest diff --git a/modules/cache/tests/cache/WincacheTest.php b/modules/cache/tests/cache/WincacheTest.php new file mode 100644 index 0000000..70e6f79 --- /dev/null +++ b/modules/cache/tests/cache/WincacheTest.php @@ -0,0 +1,39 @@ +markTestSkipped('Wincache PHP Extension is not available'); + } + + $this->cache(Cache::instance('wincache')); + } + +} // End Kohana_WincacheTest diff --git a/modules/cache/tests/cache/arithmetic/ApcTest.php b/modules/cache/tests/cache/arithmetic/ApcTest.php new file mode 100644 index 0000000..e1597cd --- /dev/null +++ b/modules/cache/tests/cache/arithmetic/ApcTest.php @@ -0,0 +1,75 @@ +markTestSkipped('APC PHP Extension is not available'); + } + + if (ini_get('apc.enable_cli') != '1') + { + $this->markTestSkipped('Unable to test APC in CLI mode. To fix '. + 'place "apc.enable_cli=1" in your php.ini file'); + } + + $this->cache(Cache::instance('apc')); + } + + /** + * Tests the [Cache::set()] method, testing; + * + * - The value is cached + * - The lifetime is respected + * - The returned value type is as expected + * - The default not-found value is respected + * + * This test doesn't test the TTL as there is a known bug/feature + * in APC that prevents the same request from killing cache on timeout. + * + * @link http://pecl.php.net/bugs/bug.php?id=16814 + * + * @dataProvider provider_set_get + * + * @param array data + * @param mixed expected + * @return void + */ + public function test_set_get(array $data, $expected) + { + if ($data['wait'] !== FALSE) + { + $this->markTestSkipped('Unable to perform TTL test in CLI, see: '. + 'http://pecl.php.net/bugs/bug.php?id=16814 for more info!'); + } + + parent::test_set_get($data, $expected); + } + +} // End Kohana_ApcTest diff --git a/modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php b/modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php new file mode 100644 index 0000000..1dcc7c7 --- /dev/null +++ b/modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php @@ -0,0 +1,173 @@ +cache(); + + if ($cache instanceof Cache) + { + $cache->delete_all(); + } + } + + /** + * Provider for test_increment + * + * @return array + */ + public function provider_increment() + { + return array( + array( + 0, + array( + 'id' => 'increment_test_1', + 'step' => 1 + ), + 1 + ), + array( + 1, + array( + 'id' => 'increment_test_2', + 'step' => 1 + ), + 2 + ), + array( + 5, + array( + 'id' => 'increment_test_3', + 'step' => 5 + ), + 10 + ), + array( + NULL, + array( + 'id' => 'increment_test_4', + 'step' => 1 + ), + FALSE + ), + ); + } + + /** + * Test for [Cache_Arithmetic::increment()] + * + * @dataProvider provider_increment + * + * @param integer start state + * @param array increment arguments + * @return void + */ + public function test_increment( + $start_state = NULL, + array $inc_args, + $expected) + { + $cache = $this->cache(); + + if ($start_state !== NULL) + { + $cache->set($inc_args['id'], $start_state, 0); + } + + $this->assertSame( + $expected, + $cache->increment( + $inc_args['id'], + $inc_args['step'] + ) + ); + } + + /** + * Provider for test_decrement + * + * @return array + */ + public function provider_decrement() + { + return array( + array( + 10, + array( + 'id' => 'decrement_test_1', + 'step' => 1 + ), + 9 + ), + array( + 10, + array( + 'id' => 'decrement_test_2', + 'step' => 2 + ), + 8 + ), + array( + 50, + array( + 'id' => 'decrement_test_3', + 'step' => 5 + ), + 45 + ), + array( + NULL, + array( + 'id' => 'decrement_test_4', + 'step' => 1 + ), + FALSE + ), + ); } + + /** + * Test for [Cache_Arithmetic::decrement()] + * + * @dataProvider provider_decrement + * + * @param integer start state + * @param array decrement arguments + * @return void + */ + public function test_decrement( + $start_state = NULL, + array $dec_args, + $expected) + { + $cache = $this->cache(); + + if ($start_state !== NULL) + { + $cache->set($dec_args['id'], $start_state, 0); + } + + $this->assertSame( + $expected, + $cache->decrement( + $dec_args['id'], + $dec_args['step'] + ) + ); + } + +} // End Kohana_CacheArithmeticMethodsTest diff --git a/modules/cache/tests/cache/arithmetic/MemcacheTest.php b/modules/cache/tests/cache/arithmetic/MemcacheTest.php new file mode 100644 index 0000000..07cb9ef --- /dev/null +++ b/modules/cache/tests/cache/arithmetic/MemcacheTest.php @@ -0,0 +1,103 @@ +markTestSkipped('Memcache PHP Extension is not available'); + } + if ( ! $config = Kohana::$config->load('cache.memcache')) + { + $this->markTestSkipped('Unable to load Memcache configuration'); + } + + $memcache = new Memcache; + if ( ! $memcache->connect($config['servers']['local']['host'], + $config['servers']['local']['port'])) + { + $this->markTestSkipped('Unable to connect to memcache server @ '. + $config['servers']['local']['host'].':'. + $config['servers']['local']['port']); + } + + if ($memcache->getVersion() === FALSE) + { + $this->markTestSkipped('Memcache server @ '. + $config['servers']['local']['host'].':'. + $config['servers']['local']['port']. + ' not responding!'); + } + + unset($memcache); + + $this->cache(Cache::instance('memcache')); + } + + /** + * Tests that multiple values set with Memcache do not cause unexpected + * results. For accurate results, this should be run with a memcache + * configuration that includes multiple servers. + * + * This is to test #4110 + * + * @link http://dev.kohanaframework.org/issues/4110 + * @return void + */ + public function test_multiple_set() + { + $cache = $this->cache(); + $id_set = 'set_id'; + $ttl = 300; + + $data = array( + 'foobar', + 0, + 1.0, + new stdClass, + array('foo', 'bar' => 1), + TRUE, + NULL, + FALSE + ); + + $previous_set = $cache->get($id_set, NULL); + + foreach ($data as $value) + { + // Use Equals over Sames as Objects will not be equal + $this->assertEquals($previous_set, $cache->get($id_set, NULL)); + $cache->set($id_set, $value, $ttl); + + $previous_set = $value; + } + } + + +} // End Kohana_CacheArithmeticMemcacheTest diff --git a/modules/cache/tests/cache/request/client/CacheTest.php b/modules/cache/tests/cache/request/client/CacheTest.php new file mode 100644 index 0000000..11f9a3e --- /dev/null +++ b/modules/cache/tests/cache/request/client/CacheTest.php @@ -0,0 +1,265 @@ +defaults(array( + 'controller' => 'welcome', + 'action' => 'index' + )); + + parent::setUp(); + } + + /** + * Tests the Client does not attempt to load cache if no Cache library + * is present + * + * @return void + */ + public function test_cache_not_called_with_no_cache() + { + $request = new Request('welcome/index'); + $response = new Response; + + $client_mock = $this->getMock('Request_Client_Internal'); + + $request->client($client_mock); + $client_mock->expects($this->exactly(0)) + ->method('execute_request'); + $client_mock->expects($this->once()) + ->method('execute') + ->will($this->returnValue($response)); + + $this->assertSame($response, $request->execute()); + } + + /** + * Tests that the client attempts to load a cached response from the + * cache library, but fails. + * + * @return void + */ + public function test_cache_miss() + { + $route = new Route('welcome/index'); + $route->defaults(array( + 'controller' => 'Kohana_Request_CacheTest_Dummy', + 'action' => 'index', + )); + + $request = new Request('welcome/index', NULL, array($route)); + $cache_mock = $this->_get_cache_mock(); + + $request->client()->cache(HTTP_Cache::factory($cache_mock)); + + $cache_mock->expects($this->once()) + ->method('get') + ->with($request->client()->cache()->create_cache_key($request)) + ->will($this->returnValue(FALSE)); + + $response = $request->client()->execute($request); + + $this->assertSame(HTTP_Cache::CACHE_STATUS_MISS, + $response->headers(HTTP_Cache::CACHE_STATUS_KEY)); + } + + /** + * Tests the client saves a response if the correct headers are set + * + * @return void + */ + public function test_cache_save() + { + $lifetime = 800; + $request = new Request('welcome/index'); + $cache_mock = $this->_get_cache_mock(); + $response = Response::factory(); + + $request->client()->cache(new HTTP_Cache(array( + 'cache' => $cache_mock + ) + )); + + $response->headers('cache-control', 'max-age='.$lifetime); + + $key = $request->client()->cache()->create_cache_key($request); + + $cache_mock->expects($this->at(0)) + ->method('set') + ->with($this->stringEndsWith($key), $this->identicalTo(0)); + + $cache_mock->expects($this->at(1)) + ->method('set') + ->with($this->identicalTo($key), $this->anything(), $this->identicalTo($lifetime)) + ->will($this->returnValue(TRUE)); + + $this->assertTrue( + $request->client()->cache() + ->cache_response($key, $request, $response) + ); + + $this->assertSame(HTTP_Cache::CACHE_STATUS_SAVED, + $response->headers(HTTP_Cache::CACHE_STATUS_KEY)); + } + + /** + * Tests the client handles a cache HIT event correctly + * + * @return void + */ + public function test_cache_hit() + { + $lifetime = 800; + $request = new Request('welcome/index'); + $cache_mock = $this->_get_cache_mock(); + + $request->client()->cache(new HTTP_Cache(array( + 'cache' => $cache_mock + ) + )); + + $response = Response::factory(); + + $response->headers(array( + 'cache-control' => 'max-age='.$lifetime, + HTTP_Cache::CACHE_STATUS_KEY => + HTTP_Cache::CACHE_STATUS_HIT + )); + + $key = $request->client()->cache()->create_cache_key($request); + + $cache_mock->expects($this->exactly(2)) + ->method('get') + ->with($this->stringContains($key)) + ->will($this->returnValue($response)); + + $request->client()->cache()->cache_response($key, $request); + + $this->assertSame(HTTP_Cache::CACHE_STATUS_HIT, + $response->headers(HTTP_Cache::CACHE_STATUS_KEY)); + } + + + /** + * Data provider for test_set_cache + * + * @return array + */ + public function provider_set_cache() + { + return array( + array( + new HTTP_Header(array('cache-control' => 'no-cache')), + array('no-cache' => NULL), + FALSE, + ), + array( + new HTTP_Header(array('cache-control' => 'no-store')), + array('no-store' => NULL), + FALSE, + ), + array( + new HTTP_Header(array('cache-control' => 'max-age=100')), + array('max-age' => '100'), + TRUE + ), + array( + new HTTP_Header(array('cache-control' => 'private')), + array('private' => NULL), + FALSE + ), + array( + new HTTP_Header(array('cache-control' => 'private, max-age=100')), + array('private' => NULL, 'max-age' => '100'), + FALSE + ), + array( + new HTTP_Header(array('cache-control' => 'private, s-maxage=100')), + array('private' => NULL, 's-maxage' => '100'), + TRUE + ), + array( + new HTTP_Header(array( + 'expires' => date('m/d/Y', strtotime('-1 day')), + )), + array(), + FALSE + ), + array( + new HTTP_Header(array( + 'expires' => date('m/d/Y', strtotime('+1 day')), + )), + array(), + TRUE + ), + array( + new HTTP_Header(array()), + array(), + TRUE + ), + ); + } + + /** + * Tests the set_cache() method + * + * @test + * @dataProvider provider_set_cache + * + * @return null + */ + public function test_set_cache($headers, $cache_control, $expected) + { + /** + * Set up a mock response object to test with + */ + $response = $this->getMock('Response'); + + $response->expects($this->any()) + ->method('headers') + ->will($this->returnValue($headers)); + + $request = new Request_Client_Internal; + $request->cache(new HTTP_Cache); + $this->assertEquals($request->cache()->set_cache($response), $expected); + } + + /** + * Returns a mock object for Cache + * + * @return Cache + */ + protected function _get_cache_mock() + { + return $this->getMock('Cache_File', array(), array(), '', FALSE); + } +} // End Kohana_Request_Client_CacheTest + +class Controller_Kohana_Request_CacheTest_Dummy extends Controller +{ + public function action_index() + { + + } +} \ No newline at end of file diff --git a/modules/cache/tests/phpunit.xml b/modules/cache/tests/phpunit.xml new file mode 100644 index 0000000..c6590be --- /dev/null +++ b/modules/cache/tests/phpunit.xml @@ -0,0 +1,19 @@ + + + ./cache + + + + + cache/ + + + diff --git a/modules/codebench/classes/Bench/ArrCallback.php b/modules/codebench/classes/Bench/ArrCallback.php new file mode 100644 index 0000000..698a6b8 --- /dev/null +++ b/modules/codebench/classes/Bench/ArrCallback.php @@ -0,0 +1,57 @@ + + */ +class Bench_ArrCallback extends Codebench { + + public $description = + 'Parsing command[param,param] strings in Arr::callback(): + http://github.com/shadowhand/kohana/commit/c3aaae849164bf92a486e29e736a265b350cb4da#L0R127'; + + public $loops = 10000; + + public $subjects = array + ( + // Valid callback strings + 'foo', + 'foo::bar', + 'foo[apple,orange]', + 'foo::bar[apple,orange]', + '[apple,orange]', // no command, only params + 'foo[[apple],[orange]]', // params with brackets inside + + // Invalid callback strings + 'foo[apple,orange', // no closing bracket + ); + + public function bench_shadowhand($subject) + { + // The original regex we're trying to optimize + if (preg_match('/([^\[]*+)\[(.*)\]/', $subject, $match)) + return $match; + } + + public function bench_geert_regex_1($subject) + { + // Added ^ and $ around the whole pattern + if (preg_match('/^([^\[]*+)\[(.*)\]$/', $subject, $matches)) + return $matches; + } + + public function bench_geert_regex_2($subject) + { + // A rather experimental approach using \K which requires PCRE 7.2 ~ PHP 5.2.4 + // Note: $matches[0] = params, $matches[1] = command + if (preg_match('/^([^\[]*+)\[\K.*(?=\]$)/', $subject, $matches)) + return $matches; + } + + public function bench_geert_str($subject) + { + // A native string function approach which beats all the regexes + if (strpos($subject, '[') !== FALSE AND substr($subject, -1) === ']') + return explode('[', substr($subject, 0, -1), 2); + } +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/AutoLinkEmails.php b/modules/codebench/classes/Bench/AutoLinkEmails.php new file mode 100644 index 0000000..46e7a15 --- /dev/null +++ b/modules/codebench/classes/Bench/AutoLinkEmails.php @@ -0,0 +1,70 @@ + + */ +class Bench_AutoLinkEmails extends Codebench { + + public $description = + 'Fixing #2772, and comparing some possibilities.'; + + public $loops = 1000; + + public $subjects = array + ( + '
    +
  • voorzitter@xxxx.com
  • +
  • vicevoorzitter@xxxx.com
  • +
', + ); + + // The original function, with str_replace replaced by preg_replace. Looks clean. + public function bench_match_all_loop($subject) + { + if (preg_match_all('~\b(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(? + */ +class Bench_DateSpan extends Codebench { + + public $description = + 'Optimization for Date::span().'; + + public $loops = 1000; + + public $subjects = array(); + + public function __construct() + { + parent::__construct(); + + $this->subjects = array( + time(), + time() - Date::MONTH, + time() - Date::YEAR, + time() - Date::YEAR * 10, + ); + } + + // Original method + public static function bench_span_original($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') + { + // Array with the output formats + $output = preg_split('/[^a-z]+/', strtolower( (string) $output)); + + // Invalid output + if (empty($output)) + return FALSE; + + // Make the output values into keys + extract(array_flip($output), EXTR_SKIP); + + if ($local === NULL) + { + // Calculate the span from the current time + $local = time(); + } + + // Calculate timespan (seconds) + $timespan = abs($remote - $local); + + if (isset($years)) + { + $timespan -= Date::YEAR * ($years = (int) floor($timespan / Date::YEAR)); + } + + if (isset($months)) + { + $timespan -= Date::MONTH * ($months = (int) floor($timespan / Date::MONTH)); + } + + if (isset($weeks)) + { + $timespan -= Date::WEEK * ($weeks = (int) floor($timespan / Date::WEEK)); + } + + if (isset($days)) + { + $timespan -= Date::DAY * ($days = (int) floor($timespan / Date::DAY)); + } + + if (isset($hours)) + { + $timespan -= Date::HOUR * ($hours = (int) floor($timespan / Date::HOUR)); + } + + if (isset($minutes)) + { + $timespan -= Date::MINUTE * ($minutes = (int) floor($timespan / Date::MINUTE)); + } + + // Seconds ago, 1 + if (isset($seconds)) + { + $seconds = $timespan; + } + + // Remove the variables that cannot be accessed + unset($timespan, $remote, $local); + + // Deny access to these variables + $deny = array_flip(array('deny', 'key', 'difference', 'output')); + + // Return the difference + $difference = array(); + foreach ($output as $key) + { + if (isset($$key) AND ! isset($deny[$key])) + { + // Add requested key to the output + $difference[$key] = $$key; + } + } + + // Invalid output formats string + if (empty($difference)) + return FALSE; + + // If only one output format was asked, don't put it in an array + if (count($difference) === 1) + return current($difference); + + // Return array + return $difference; + } + + // Using an array for the output + public static function bench_span_use_array($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') + { + // Array with the output formats + $output = preg_split('/[^a-z]+/', strtolower( (string) $output)); + + // Invalid output + if (empty($output)) + return FALSE; + + // Convert the list of outputs to an associative array + $output = array_combine($output, array_fill(0, count($output), 0)); + + // Make the output values into keys + extract(array_flip($output), EXTR_SKIP); + + if ($local === NULL) + { + // Calculate the span from the current time + $local = time(); + } + + // Calculate timespan (seconds) + $timespan = abs($remote - $local); + + if (isset($output['years'])) + { + $timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR)); + } + + if (isset($output['months'])) + { + $timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH)); + } + + if (isset($output['weeks'])) + { + $timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK)); + } + + if (isset($output['days'])) + { + $timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY)); + } + + if (isset($output['hours'])) + { + $timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR)); + } + + if (isset($output['minutes'])) + { + $timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE)); + } + + // Seconds ago, 1 + if (isset($output['seconds'])) + { + $output['seconds'] = $timespan; + } + + if (count($output) === 1) + { + // Only a single output was requested, return it + return array_pop($output); + } + + // Return array + return $output; + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/ExplodeLimit.php b/modules/codebench/classes/Bench/ExplodeLimit.php new file mode 100644 index 0000000..4bf2acc --- /dev/null +++ b/modules/codebench/classes/Bench/ExplodeLimit.php @@ -0,0 +1,34 @@ + + */ +class Bench_ExplodeLimit extends Codebench { + + public $description = + 'Having a look at the effect of adding a limit to the explode function.
+ http://stackoverflow.com/questions/1308149/how-to-get-a-part-of-url-between-4th-and-5th-slashes'; + + public $loops = 10000; + + public $subjects = array + ( + 'http://example.com/articles/123a/view', + 'http://example.com/articles/123a/view/x/x/x/x/x', + 'http://example.com/articles/123a/view/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x', + ); + + public function bench_explode_without_limit($subject) + { + $parts = explode('/', $subject); + return $parts[4]; + } + + public function bench_explode_with_limit($subject) + { + $parts = explode('/', $subject, 6); + return $parts[4]; + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/GruberURL.php b/modules/codebench/classes/Bench/GruberURL.php new file mode 100644 index 0000000..af23975 --- /dev/null +++ b/modules/codebench/classes/Bench/GruberURL.php @@ -0,0 +1,61 @@ + + */ +class Bench_GruberURL extends Codebench { + + public $description = + 'Optimization for http://daringfireball.net/2009/11/liberal_regex_for_matching_urls'; + + public $loops = 10000; + + public $subjects = array + ( + 'http://foo.com/blah_blah', + 'http://foo.com/blah_blah/', + '(Something like http://foo.com/blah_blah)', + 'http://foo.com/blah_blah_(wikipedia)', + '(Something like http://foo.com/blah_blah_(wikipedia))', + 'http://foo.com/blah_blah.', + 'http://foo.com/blah_blah/.', + '', + '', + 'http://foo.com/blah_blah,', + 'http://www.example.com/wpstyle/?p=364.', + 'http://✪df.ws/e7l', + 'rdar://1234', + 'rdar:/1234', + 'x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E', + 'message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e', + 'http://➡.ws/䨹', + 'www.➡.ws/䨹', + 'http://example.com', + 'Just a www.example.com link.', + // To test the use of possessive quatifiers: + 'httpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp', + ); + + public function bench_daringfireball($subject) + { + // Original regex by John Gruber + preg_match('~\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))~', $subject, $matches); + return (empty($matches)) ? FALSE : $matches[0]; + } + + public function bench_daringfireball_v2($subject) + { + // Removed outer capturing parentheses, made another pair non-capturing + preg_match('~\b(?:[\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|(?:[^[:punct:]\s]|/))~', $subject, $matches); + return (empty($matches)) ? FALSE : $matches[0]; + } + + public function bench_daringfireball_v3($subject) + { + // Made quantifiers possessive where possible + preg_match('~\b(?:[\w-]++://?+|www[.])[^\s()<>]+(?:\([\w\d]++\)|(?:[^[:punct:]\s]|/))~', $subject, $matches); + return (empty($matches)) ? FALSE : $matches[0]; + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/LtrimDigits.php b/modules/codebench/classes/Bench/LtrimDigits.php new file mode 100644 index 0000000..71ead49 --- /dev/null +++ b/modules/codebench/classes/Bench/LtrimDigits.php @@ -0,0 +1,28 @@ + + */ +class Bench_LtrimDigits extends Codebench { + + public $description = 'Chopping off leading digits: regex vs ltrim.'; + + public $loops = 100000; + + public $subjects = array + ( + '123digits', + 'no-digits', + ); + + public function bench_regex($subject) + { + return preg_replace('/^\d+/', '', $subject); + } + + public function bench_ltrim($subject) + { + return ltrim($subject, '0..9'); + } +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/MDDoBaseURL.php b/modules/codebench/classes/Bench/MDDoBaseURL.php new file mode 100644 index 0000000..1ad2a1b --- /dev/null +++ b/modules/codebench/classes/Bench/MDDoBaseURL.php @@ -0,0 +1,66 @@ + + */ +class Bench_MDDoBaseURL extends Codebench { + + public $description = + 'Optimization for the doBaseURL() method of Kohana_Kodoc_Markdown + for the Kohana Userguide.'; + + public $loops = 10000; + + public $subjects = array + ( + // Valid matches + '[filesystem](about.filesystem)', + '[filesystem](about.filesystem "Optional title")', + '[same page link](#id)', + '[object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming)', + + // Invalid matches + '![this is image syntax](about.filesystem)', + '[filesystem](about.filesystem', + ); + + public function bench_original($subject) + { + // The original regex contained a bug, which is fixed here for benchmarking purposes. + // At the very start of the regex, (?!!) has been replace by (? + */ +class Bench_MDDoImageURL extends Codebench { + + public $description = + 'Optimization for the doImageURL() method of Kohana_Kodoc_Markdown + for the Kohana Userguide.'; + + public $loops = 10000; + + public $subjects = array + ( + // Valid matches + '![Alt text](http://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)', + '![Alt text](https://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)', + '![Alt text](otherprotocol://image.png "Optional title")', + '![Alt text](img/install.png "Optional title")', + '![Alt text containing [square] brackets](img/install.png)', + '![Empty src]()', + + // Invalid matches + '![Alt text](img/install.png "No closing parenthesis"', + ); + + public function bench_original($subject) + { + return preg_replace_callback('~!\[(.+?)\]\((\S*(?:\s*".+?")?)\)~', array($this, '_add_image_url_original'), $subject); + } + protected function _add_image_url_original($matches) + { + if ($matches[2] AND strpos($matches[2], '://') === FALSE) + { + // Add the base url to the link URL + $matches[2] = 'http://BASE/'.$matches[2]; + } + + // Recreate the link + return "![{$matches[1]}]({$matches[2]})"; + } + + public function bench_optimized_callback($subject) + { + // Moved the check for "://" to the regex, simplifying the callback function + return preg_replace_callback('~!\[(.+?)\]\((?!\w++://)(\S*(?:\s*+".+?")?)\)~', array($this, '_add_image_url_optimized'), $subject); + } + protected function _add_image_url_optimized($matches) + { + // Add the base url to the link URL + $matches[2] = 'http://BASE/'.$matches[2]; + + // Recreate the link + return "![{$matches[1]}]({$matches[2]})"; + } + + public function bench_callback_gone($subject) + { + // All the optimized callback was doing now, is prepend some text to the URL. + // We don't need a callback for that, and that should be clearly faster. + return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1http://BASE/$2', $subject); + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/MDDoIncludeViews.php b/modules/codebench/classes/Bench/MDDoIncludeViews.php new file mode 100644 index 0000000..9cac3d6 --- /dev/null +++ b/modules/codebench/classes/Bench/MDDoIncludeViews.php @@ -0,0 +1,50 @@ + + */ +class Bench_MDDoIncludeViews extends Codebench { + + public $description = + 'Optimization for the doIncludeViews() method of Kohana_Kodoc_Markdown + for the Kohana Userguide.'; + + public $loops = 10000; + + public $subjects = array + ( + // Valid matches + '{{one}} two {{three}}', + '{{userguide/examples/hello_world_error}}', + + // Invalid matches + '{}', + '{{}}', + '{{userguide/examples/hello_world_error}', + '{{userguide/examples/hello_world_error }}', + '{{userguide/examples/{{hello_world_error }}', + ); + + public function bench_original($subject) + { + preg_match_all('/{{(\S+?)}}/m', $subject, $matches, PREG_SET_ORDER); + return $matches; + } + + public function bench_possessive($subject) + { + // Using a possessive character class + // Removed useless /m modifier + preg_match_all('/{{([^\s{}]++)}}/', $subject, $matches, PREG_SET_ORDER); + return $matches; + } + + public function bench_lookaround($subject) + { + // Using lookaround to move $mathes[1] into $matches[0] + preg_match_all('/(?<={{)[^\s{}]++(?=}})/', $subject, $matches, PREG_SET_ORDER); + return $matches; + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/StripNullBytes.php b/modules/codebench/classes/Bench/StripNullBytes.php new file mode 100644 index 0000000..4d28853 --- /dev/null +++ b/modules/codebench/classes/Bench/StripNullBytes.php @@ -0,0 +1,37 @@ + + */ +class Bench_StripNullBytes extends Codebench { + + public $description = + 'String replacement comparisons related to #2676.'; + + public $loops = 1000; + + public $subjects = array + ( + "\0", + "\0\0\0\0\0\0\0\0\0\0", + "bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla", + "blablablablablablablablablablablablablablablabla", + ); + + public function bench_str_replace($subject) + { + return str_replace("\0", '', $subject); + } + + public function bench_strtr($subject) + { + return strtr($subject, array("\0" => '')); + } + + public function bench_preg_replace($subject) + { + return preg_replace('~\0+~', '', $subject); + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/Transliterate.php b/modules/codebench/classes/Bench/Transliterate.php new file mode 100644 index 0000000..aff8693 --- /dev/null +++ b/modules/codebench/classes/Bench/Transliterate.php @@ -0,0 +1,65 @@ + + */ +class Bench_Transliterate extends Codebench { + + public $description = + 'Inspired by: + http://forum.kohanaframework.org/comments.php?DiscussionID=6113'; + + public $loops = 10; + + public $subjects = array + ( + // ASCII + 'a', 'b', 'c', 'd', '1', '2', '3', + + // Non-ASCII + 'à', 'ô', 'ď', 'ḟ', 'ë', 'š', 'ơ', + 'ß', 'ă', 'ř', 'ț', 'ň', 'ā', 'ķ', + 'ŝ', 'ỳ', 'ņ', 'ĺ', 'ħ', 'ṗ', 'ó', + 'ú', 'ě', 'é', 'ç', 'ẁ', 'ċ', 'õ', + 'ṡ', 'ø', 'ģ', 'ŧ', 'ș', 'ė', 'ĉ', + 'ś', 'î', 'ű', 'ć', 'ę', 'ŵ', 'ṫ', + 'ū', 'č', 'ö', 'è', 'ŷ', 'ą', 'ł', + 'ų', 'ů', 'ş', 'ğ', 'ļ', 'ƒ', 'ž', + 'ẃ', 'ḃ', 'å', 'ì', 'ï', 'ḋ', 'ť', + 'ŗ', 'ä', 'í', 'ŕ', 'ê', 'ü', 'ò', + 'ē', 'ñ', 'ń', 'ĥ', 'ĝ', 'đ', 'ĵ', + 'ÿ', 'ũ', 'ŭ', 'ư', 'ţ', 'ý', 'ő', + 'â', 'ľ', 'ẅ', 'ż', 'ī', 'ã', 'ġ', + 'ṁ', 'ō', 'ĩ', 'ù', 'į', 'ź', 'á', + 'û', 'þ', 'ð', 'æ', 'µ', 'ĕ', 'ı', + 'À', 'Ô', 'Ď', 'Ḟ', 'Ë', 'Š', 'Ơ', + 'Ă', 'Ř', 'Ț', 'Ň', 'Ā', 'Ķ', 'Ĕ', + 'Ŝ', 'Ỳ', 'Ņ', 'Ĺ', 'Ħ', 'Ṗ', 'Ó', + 'Ú', 'Ě', 'É', 'Ç', 'Ẁ', 'Ċ', 'Õ', + 'Ṡ', 'Ø', 'Ģ', 'Ŧ', 'Ș', 'Ė', 'Ĉ', + 'Ś', 'Î', 'Ű', 'Ć', 'Ę', 'Ŵ', 'Ṫ', + 'Ū', 'Č', 'Ö', 'È', 'Ŷ', 'Ą', 'Ł', + 'Ų', 'Ů', 'Ş', 'Ğ', 'Ļ', 'Ƒ', 'Ž', + 'Ẃ', 'Ḃ', 'Å', 'Ì', 'Ï', 'Ḋ', 'Ť', + 'Ŗ', 'Ä', 'Í', 'Ŕ', 'Ê', 'Ü', 'Ò', + 'Ē', 'Ñ', 'Ń', 'Ĥ', 'Ĝ', 'Đ', 'Ĵ', + 'Ÿ', 'Ũ', 'Ŭ', 'Ư', 'Ţ', 'Ý', 'Ő', + 'Â', 'Ľ', 'Ẅ', 'Ż', 'Ī', 'Ã', 'Ġ', + 'Ṁ', 'Ō', 'Ĩ', 'Ù', 'Į', 'Ź', 'Á', + 'Û', 'Þ', 'Ð', 'Æ', 'İ', + ); + + public function bench_utf8($subject) + { + return UTF8::transliterate_to_ascii($subject); + } + + public function bench_iconv($subject) + { + // Note: need to suppress errors on iconv because some chars trigger the following notice: + // "Detected an illegal character in input string" + return preg_replace('~[^-a-z0-9]+~i', '', @iconv('UTF-8', 'ASCII//TRANSLIT', $subject)); + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/URLSite.php b/modules/codebench/classes/Bench/URLSite.php new file mode 100644 index 0000000..0db347d --- /dev/null +++ b/modules/codebench/classes/Bench/URLSite.php @@ -0,0 +1,123 @@ + + */ +class Bench_URLSite extends Codebench { + + public $description = 'http://dev.kohanaframework.org/issues/3110'; + + public $loops = 1000; + + public $subjects = array + ( + '', + 'news', + 'news/', + '/news/', + 'news/page/5', + 'news/page:5', + 'http://example.com/', + 'http://example.com/hello', + 'http://example.com:80/', + 'http://user:pass@example.com/', + ); + + public function __construct() + { + foreach ($this->subjects as $subject) + { + // Automatically create URIs with query string and/or fragment part appended + $this->subjects[] = $subject.'?query=string'; + $this->subjects[] = $subject.'#fragment'; + $this->subjects[] = $subject.'?query=string#fragment'; + } + + parent::__construct(); + } + + public function bench_original($uri) + { + // Get the path from the URI + $path = trim(parse_url($uri, PHP_URL_PATH), '/'); + + if ($query = parse_url($uri, PHP_URL_QUERY)) + { + $query = '?'.$query; + } + + if ($fragment = parse_url($uri, PHP_URL_FRAGMENT)) + { + $fragment = '#'.$fragment; + } + + return $path.$query.$fragment; + } + + public function bench_explode($uri) + { + // Chop off possible scheme, host, port, user and pass parts + $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); + + $fragment = ''; + $explode = explode('#', $path, 2); + if (isset($explode[1])) + { + $path = $explode[0]; + $fragment = '#'.$explode[1]; + } + + $query = ''; + $explode = explode('?', $path, 2); + if (isset($explode[1])) + { + $path = $explode[0]; + $query = '?'.$explode[1]; + } + + return $path.$query.$fragment; + } + + public function bench_regex($uri) + { + preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches); + $path = Arr::get($matches, 1, ''); + $query = Arr::get($matches, 2, ''); + $fragment = Arr::get($matches, 3, ''); + + return $path.$query.$fragment; + } + + public function bench_regex_without_arrget($uri) + { + preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches); + $path = isset($matches[1]) ? $matches[1] : ''; + $query = isset($matches[2]) ? $matches[2] : ''; + $fragment = isset($matches[3]) ? $matches[3] : ''; + + return $path.$query.$fragment; + } + + // And then I thought, why do all the work of extracting the query and fragment parts and then reappending them? + // Just leaving them alone should be fine, right? As a bonus we get a very nice speed boost. + public function bench_less_is_more($uri) + { + // Chop off possible scheme, host, port, user and pass parts + $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); + + return $path; + } + + public function bench_less_is_more_with_strpos_optimization($uri) + { + if (strpos($uri, '://') !== FALSE) + { + // Chop off possible scheme, host, port, user and pass parts + $uri = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); + } + + return $uri; + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/UserFuncArray.php b/modules/codebench/classes/Bench/UserFuncArray.php new file mode 100644 index 0000000..f53d0c6 --- /dev/null +++ b/modules/codebench/classes/Bench/UserFuncArray.php @@ -0,0 +1,58 @@ + + */ +class Bench_UserFuncArray extends Codebench { + + public $description = + 'Testing the speed difference of using call_user_func_array + compared to counting args and doing manual calls.'; + + public $loops = 100000; + + public $subjects = array + ( + // Argument sets + array(), + array('one'), + array('one', 'two'), + array('one', 'two', 'three'), + ); + + public function bench_count_args($args) + { + $name = 'callme'; + switch (count($args)) + { + case 1: + $this->$name($args[0]); + break; + case 2: + $this->$name($args[0], $args[1]); + break; + case 3: + $this->$name($args[0], $args[1], $args[2]); + break; + case 4: + $this->$name($args[0], $args[1], $args[2], $args[3]); + break; + default: + call_user_func_array(array($this, $name), $args); + break; + } + } + + public function bench_direct_call($args) + { + $name = 'callme'; + call_user_func_array(array($this, $name), $args); + } + + protected function callme() + { + return count(func_get_args()); + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/ValidColor.php b/modules/codebench/classes/Bench/ValidColor.php new file mode 100644 index 0000000..8d04608 --- /dev/null +++ b/modules/codebench/classes/Bench/ValidColor.php @@ -0,0 +1,116 @@ + + */ +class Bench_ValidColor extends Codebench { + + public $description = + 'Optimization for Validate::color(). + See: http://forum.kohanaphp.com/comments.php?DiscussionID=2192. + + Note that the methods with an _invalid suffix contain flawed regexes and should be + completely discarded. I left them in here for educational purposes, and to remind myself + to think harder and test more thoroughly. It can\'t be that I only found out so late in + the game. For the regex explanation have a look at the forum topic mentioned earlier.'; + + public $loops = 10000; + + public $subjects = array + ( + // Valid colors + 'aaA', + '123', + '000000', + '#123456', + '#abcdef', + + // Invalid colors + 'ggg', + '1234', + '#1234567', + "#000\n", + '}§è!çà%$z', + ); + + // Note that I added the D modifier to corey's regexes. We need to match exactly + // the same if we want the benchmarks to be of any value. + public function bench_corey_regex_1_invalid($subject) + { + return (bool) preg_match('/^#?([0-9a-f]{1,2}){3}$/iD', $subject); + } + + public function bench_corey_regex_2($subject) + { + return (bool) preg_match('/^#?([0-9a-f]){3}(([0-9a-f]){3})?$/iD', $subject); + } + + // Optimized corey_regex_1 + // Using non-capturing parentheses and a possessive interval + public function bench_geert_regex_1a_invalid($subject) + { + return (bool) preg_match('/^#?(?:[0-9a-f]{1,2}+){3}$/iD', $subject); + } + + // Optimized corey_regex_2 + // Removed useless parentheses, made the remaining ones non-capturing + public function bench_geert_regex_2a($subject) + { + return (bool) preg_match('/^#?[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject); + } + + // Optimized geert_regex_1a + // Possessive "#" + public function bench_geert_regex_1b_invalid($subject) + { + return (bool) preg_match('/^#?+(?:[0-9a-f]{1,2}+){3}$/iD', $subject); + } + + // Optimized geert_regex_2a + // Possessive "#" + public function bench_geert_regex_2b($subject) + { + return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject); + } + + // Using \z instead of $ + public function bench_salathe_regex_1($subject) + { + return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject); + } + + // Using \A instead of ^ + public function bench_salathe_regex_2($subject) + { + return (bool) preg_match('/\A#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject); + } + + // A solution without regex + public function bench_geert_str($subject) + { + if ($subject[0] === '#') + { + $subject = substr($subject, 1); + } + + $strlen = strlen($subject); + return (($strlen === 3 OR $strlen === 6) AND ctype_xdigit($subject)); + } + + // An ugly, but fast, solution without regex + public function bench_salathe_str($subject) + { + if ($subject[0] === '#') + { + $subject = substr($subject, 1); + } + + // TRUE if: + // 1. $subject is 6 or 3 chars long + // 2. $subject contains only hexadecimal digits + return (((isset($subject[5]) AND ! isset($subject[6])) OR + (isset($subject[2]) AND ! isset($subject[3]))) + AND ctype_xdigit($subject)); + } +} \ No newline at end of file diff --git a/modules/codebench/classes/Bench/ValidURL.php b/modules/codebench/classes/Bench/ValidURL.php new file mode 100644 index 0000000..3c88675 --- /dev/null +++ b/modules/codebench/classes/Bench/ValidURL.php @@ -0,0 +1,105 @@ + + */ +class Bench_ValidURL extends Codebench { + + public $description = + 'filter_var vs regex: + http://dev.kohanaframework.org/issues/2847'; + + public $loops = 1000; + + public $subjects = array + ( + // Valid + 'http://google.com', + 'http://google.com/', + 'http://google.com/?q=abc', + 'http://google.com/#hash', + 'http://localhost', + 'http://hello-world.pl', + 'http://hello--world.pl', + 'http://h.e.l.l.0.pl', + 'http://server.tld/get/info', + 'http://127.0.0.1', + 'http://127.0.0.1:80', + 'http://user@127.0.0.1', + 'http://user:pass@127.0.0.1', + 'ftp://my.server.com', + 'rss+xml://rss.example.com', + + // Invalid + 'http://google.2com', + 'http://google.com?q=abc', + 'http://google.com#hash', + 'http://hello-.pl', + 'http://hel.-lo.world.pl', + 'http://ww£.google.com', + 'http://127.0.0.1234', + 'http://127.0.0.1.1', + 'http://user:@127.0.0.1', + "http://finalnewline.com\n", + ); + + public function bench_filter_var($url) + { + return (bool) filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED); + } + + public function bench_regex($url) + { + // Based on http://www.apps.ietf.org/rfc/rfc1738.html#sec-5 + if ( ! preg_match( + '~^ + + # scheme + [-a-z0-9+.]++:// + + # username:password (optional) + (?: + [-a-z0-9$_.+!*\'(),;?&=%]++ # username + (?::[-a-z0-9$_.+!*\'(),;?&=%]++)? # password (optional) + @ + )? + + (?: + # ip address + \d{1,3}+(?:\.\d{1,3}+){3}+ + + | # or + + # hostname (captured) + ( + (?!-)[-a-z0-9]{1,63}+(? 253) + return FALSE; + + // An extra check for the top level domain + // It must start with a letter + $tld = ltrim(substr($matches[1], (int) strrpos($matches[1], '.')), '.'); + return ctype_alpha($tld[0]); + } + +} \ No newline at end of file diff --git a/modules/codebench/classes/Codebench.php b/modules/codebench/classes/Codebench.php new file mode 100644 index 0000000..0f4c55c --- /dev/null +++ b/modules/codebench/classes/Codebench.php @@ -0,0 +1,3 @@ +request->param('class'); + + // Convert submitted class name to URI segment + if (isset($_POST['class'])) + { + throw HTTP_Exception::factory(302)->location('codebench/'.trim($_POST['class'])); + } + + // Pass the class name on to the view + $this->template->class = (string) $class; + + // Try to load the class, then run it + if (Kohana::auto_load($class) === TRUE) + { + $codebench = new $class; + $this->template->codebench = $codebench->run(); + } + } +} diff --git a/modules/codebench/classes/Kohana/Codebench.php b/modules/codebench/classes/Kohana/Codebench.php new file mode 100644 index 0000000..0acfa5c --- /dev/null +++ b/modules/codebench/classes/Kohana/Codebench.php @@ -0,0 +1,217 @@ + 'A', + 150 => 'B', + 200 => 'C', + 300 => 'D', + 500 => 'E', + 'default' => 'F', + ); + + /** + * Constructor. + * + * @return void + */ + public function __construct() + { + // Set the maximum execution time + set_time_limit(Kohana::$config->load('codebench')->max_execution_time); + } + + /** + * Runs Codebench on the extending class. + * + * @return array benchmark output + */ + public function run() + { + // Array of all methods to loop over + $methods = array_filter(get_class_methods($this), array($this, '_method_filter')); + + // Make sure the benchmark runs at least once, + // also if no subject data has been provided. + if (empty($this->subjects)) + { + $this->subjects = array('NULL' => NULL); + } + + // Initialize benchmark output + $codebench = array + ( + 'class' => get_class($this), + 'description' => $this->description, + 'loops' => array + ( + 'base' => (int) $this->loops, + 'total' => (int) $this->loops * count($this->subjects) * count($methods), + ), + 'subjects' => $this->subjects, + 'benchmarks' => array(), + ); + + // Benchmark each method + foreach ($methods as $method) + { + // Initialize benchmark output for this method + $codebench['benchmarks'][$method] = array('time' => 0, 'memory' => 0); + + // Using Reflection because simply calling $this->$method($subject) in the loop below + // results in buggy benchmark times correlating to the length of the method name. + $reflection = new ReflectionMethod(get_class($this), $method); + + // Benchmark each subject on each method + foreach ($this->subjects as $subject_key => $subject) + { + // Prerun each method/subject combo before the actual benchmark loop. + // This way relatively expensive initial processes won't be benchmarked, e.g. autoloading. + // At the same time we capture the return here so we don't have to do that in the loop anymore. + $return = $reflection->invoke($this, $subject); + + // Start the timer for one subject + $token = Profiler::start('codebench', $method.$subject_key); + + // The heavy work + for ($i = 0; $i < $this->loops; ++$i) + { + $reflection->invoke($this, $subject); + } + + // Stop and read the timer + $benchmark = Profiler::total($token); + + // Benchmark output specific to the current method and subject + $codebench['benchmarks'][$method]['subjects'][$subject_key] = array + ( + 'return' => $return, + 'time' => $benchmark[0], + 'memory' => $benchmark[1], + ); + + // Update method totals + $codebench['benchmarks'][$method]['time'] += $benchmark[0]; + $codebench['benchmarks'][$method]['memory'] += $benchmark[1]; + } + } + + // Initialize the fastest and slowest benchmarks for both methods and subjects, time and memory, + // these values will be overwritten using min() and max() later on. + // The 999999999 values look like a hack, I know, but they work, + // unless your method runs for more than 31 years or consumes over 1GB of memory. + $fastest_method = $fastest_subject = array('time' => 999999999, 'memory' => 999999999); + $slowest_method = $slowest_subject = array('time' => 0, 'memory' => 0); + + // Find the fastest and slowest benchmarks, needed for the percentage calculations + foreach ($methods as $method) + { + // Update the fastest and slowest method benchmarks + $fastest_method['time'] = min($fastest_method['time'], $codebench['benchmarks'][$method]['time']); + $fastest_method['memory'] = min($fastest_method['memory'], $codebench['benchmarks'][$method]['memory']); + $slowest_method['time'] = max($slowest_method['time'], $codebench['benchmarks'][$method]['time']); + $slowest_method['memory'] = max($slowest_method['memory'], $codebench['benchmarks'][$method]['memory']); + + foreach ($this->subjects as $subject_key => $subject) + { + // Update the fastest and slowest subject benchmarks + $fastest_subject['time'] = min($fastest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']); + $fastest_subject['memory'] = min($fastest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']); + $slowest_subject['time'] = max($slowest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']); + $slowest_subject['memory'] = max($slowest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']); + } + } + + // Percentage calculations for methods + foreach ($codebench['benchmarks'] as & $method) + { + // Calculate percentage difference relative to fastest and slowest methods + $method['percent']['fastest']['time'] = (empty($fastest_method['time'])) ? 0 : ($method['time'] / $fastest_method['time'] * 100); + $method['percent']['fastest']['memory'] = (empty($fastest_method['memory'])) ? 0 : ($method['memory'] / $fastest_method['memory'] * 100); + $method['percent']['slowest']['time'] = (empty($slowest_method['time'])) ? 0 : ($method['time'] / $slowest_method['time'] * 100); + $method['percent']['slowest']['memory'] = (empty($slowest_method['memory'])) ? 0 : ($method['memory'] / $slowest_method['memory'] * 100); + + // Assign a grade for time and memory to each method + $method['grade']['time'] = $this->_grade($method['percent']['fastest']['time']); + $method['grade']['memory'] = $this->_grade($method['percent']['fastest']['memory']); + + // Percentage calculations for subjects + foreach ($method['subjects'] as & $subject) + { + // Calculate percentage difference relative to fastest and slowest subjects for this method + $subject['percent']['fastest']['time'] = (empty($fastest_subject['time'])) ? 0 : ($subject['time'] / $fastest_subject['time'] * 100); + $subject['percent']['fastest']['memory'] = (empty($fastest_subject['memory'])) ? 0 : ($subject['memory'] / $fastest_subject['memory'] * 100); + $subject['percent']['slowest']['time'] = (empty($slowest_subject['time'])) ? 0 : ($subject['time'] / $slowest_subject['time'] * 100); + $subject['percent']['slowest']['memory'] = (empty($slowest_subject['memory'])) ? 0 : ($subject['memory'] / $slowest_subject['memory'] * 100); + + // Assign a grade letter for time and memory to each subject + $subject['grade']['time'] = $this->_grade($subject['percent']['fastest']['time']); + $subject['grade']['memory'] = $this->_grade($subject['percent']['fastest']['memory']); + } + } + + return $codebench; + } + + /** + * Callback for array_filter(). + * Filters out all methods not to benchmark. + * + * @param string method name + * @return boolean + */ + protected function _method_filter($method) + { + // Only benchmark methods with the "bench" prefix + return (substr($method, 0, 5) === 'bench'); + } + + /** + * Returns the applicable grade letter for a score. + * + * @param integer|double score + * @return string grade letter + */ + protected function _grade($score) + { + foreach ($this->grades as $max => $grade) + { + if ($max === 'default') + continue; + + if ($score <= $max) + return $grade; + } + + return $this->grades['default']; + } +} diff --git a/modules/codebench/config/codebench.php b/modules/codebench/config/codebench.php new file mode 100644 index 0000000..590186d --- /dev/null +++ b/modules/codebench/config/codebench.php @@ -0,0 +1,16 @@ + 0, + + /** + * Expand all benchmark details by default. + */ + 'expand_all' => FALSE, + +); diff --git a/modules/codebench/config/userguide.php b/modules/codebench/config/userguide.php new file mode 100644 index 0000000..e6943ac --- /dev/null +++ b/modules/codebench/config/userguide.php @@ -0,0 +1,23 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'codebench' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'Codebench', + + // A short description of this module, shown on the index page + 'description' => 'Code benchmarking tool.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2012 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/modules/codebench/guide/codebench/index.md b/modules/codebench/guide/codebench/index.md new file mode 100644 index 0000000..78b6f21 --- /dev/null +++ b/modules/codebench/guide/codebench/index.md @@ -0,0 +1,76 @@ +# Using Codebench + +[!!] The contents of this page are taken (with some minor changes) from and are copyright Geert De Deckere. + +For a long time I have been using a quick-and-dirty `benchmark.php` file to optimize bits of PHP code, many times regex-related stuff. The file contained not much more than a [gettimeofday](http://php.net/gettimeofday) function wrapped around a `for` loop. It worked, albeit not very efficiently. Something more solid was needed. I set out to create a far more usable piece of software to aid in the everlasting quest to squeeze every millisecond out of those regular expressions. + +## Codebench Goals + +### Benchmark multiple regular expressions at once + +Being able to compare the speed of an arbitrary amount of regular expressions would be tremendously useful. In case you are wondering—yes, I had been writing down benchmark times for each regex, uncommenting them one by one. You get the idea. Those days should be gone forever now. + +### Benchmark multiple subjects at once + +What gets overlooked too often when testing and optimizing regular expressions is the fact that speed can vastly differ depending on the subjects, also known as input or target strings. Just because your regular expression matches, say, a valid email address quickly, does not necessarily mean it will quickly realize when an invalid email is provided. I plan to write a follow-up article with hands-on regex examples to demonstrate this point. Anyway, Codebench allows you to create an array of subjects which will be passed to each benchmark. + +### Make it flexible enough to work for all PCRE functions + +Initially I named the module “Regexbench”. I quickly realized, though, it would be flexible enough to benchmark all kinds of PHP code, hence the change to “Codebench”. While tools specifically built to help profiling PCRE functions, like [preg_match](http://php.net/preg_match) or [preg_replace](http://php.net/preg_replace), definitely have their use, more flexibility was needed here. You should be able to compare all kinds of constructions like combinations of PCRE functions and native PHP string functions. + +### Create clean and portable benchmark cases + +Throwing valuable benchmark data away every time I needed to optimize another regular expression had to stop. A clean file containing the complete set of all regex variations to compare, together with the set of subjects to test them against, would be more than welcome. Moreover, it would be easy to exchange benchmark cases with others. + +### Visualize the benchmarks + +Obviously providing a visual representation of the benchmark results, via simple graphs, would make interpreting them easier. Having not to think about Internet Explorer for once, made writing CSS a whole lot more easy and fun. It resulted in some fine graphs which are fully resizable. + +Below are two screenshots of Codebench in action. `Valid_Color` is a class made for benchmarking different ways to validate hexadecimal HTML color values, e.g. `#FFF`. If you are interested in the story behind the actual regular expressions, take a look at [this topic in the Kohana forums](http://forum.kohanaphp.com/comments.php?DiscussionID=2192). + +![Benchmarking several ways to validate HTML color values](codebench_screenshot1.png) +**Benchmarking seven ways to validate HTML color values** + +![Collapsable results per subject for each method](codebench_screenshot2.png) +**Collapsable results per subject for each method** + +## Working with Codebench + +Codebench is included in Kohana 3, but if you need you [can download it](http://github.com/kohana/codebench/) from GitHub. Be sure Codebench is activated in your `application/bootstrap.php`. + +Creating your own benchmarks is just a matter of creating a class that extends the Codebench class. The class should go in `classes/bench` and the class name should have the `Bench_` prefix. Put the code parts you want to compare into separate methods. Be sure to prefix those methods with `bench_`, other methods will not be benchmarked. Glance at the files in `modules/codebench/classes/bench/` for more examples. + +Here is another short example with some extra explanations. + + // classes/bench/ltrimdigits.php + class Bench_LtrimDigits extends Codebench { + + // Some optional explanatory comments about the benchmark file. + // HTML allowed. URLs will be converted to links automatically. + public $description = 'Chopping off leading digits: regex vs ltrim.'; + + // How many times to execute each method per subject. + // Total loops = loops * number of methods * number of subjects + public $loops = 100000; + + // The subjects to supply iteratively to your benchmark methods. + public $subjects = array + ( + '123digits', + 'no-digits', + ); + + public function bench_regex($subject) + { + return preg_replace('/^\d+/', '', $subject); + } + + public function bench_ltrim($subject) + { + return ltrim($subject, '0..9'); + } + } + + + +And the winner is… [ltrim](http://php.net/ltrim). Happy benchmarking! \ No newline at end of file diff --git a/modules/codebench/guide/codebench/menu.md b/modules/codebench/guide/codebench/menu.md new file mode 100644 index 0000000..c73a19a --- /dev/null +++ b/modules/codebench/guide/codebench/menu.md @@ -0,0 +1 @@ +## [Codebench]() \ No newline at end of file diff --git a/modules/codebench/init.php b/modules/codebench/init.php new file mode 100644 index 0000000..f238cb0 --- /dev/null +++ b/modules/codebench/init.php @@ -0,0 +1,8 @@ +)') + ->defaults(array( + 'controller' => 'Codebench', + 'action' => 'index', + 'class' => NULL)); diff --git a/modules/codebench/media/guide/codebench/codebench_screenshot1.png b/modules/codebench/media/guide/codebench/codebench_screenshot1.png new file mode 100644 index 0000000000000000000000000000000000000000..d1cfcfb2cc4db8afe79465408be1c0587244b780 GIT binary patch literal 15827 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 X%k+Esjeze-6WmwTQ7Kn?68`@HDeN9w literal 0 HcmV?d00001 diff --git a/modules/codebench/media/guide/codebench/codebench_screenshot2.png b/modules/codebench/media/guide/codebench/codebench_screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..85bf560bead5dd6042892a5a90f2d1212a7c8ee0 GIT binary patch literal 13575 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 literal 0 HcmV?d00001 diff --git a/modules/codebench/views/codebench.php b/modules/codebench/views/codebench.php new file mode 100644 index 0000000..6482340 --- /dev/null +++ b/modules/codebench/views/codebench.php @@ -0,0 +1,260 @@ + + + + + + + + <?php if ($class !== ''): ?> + <?php echo $class, ' · ' ?> + <?php endif; ?>Codebench + + + + + + + + + + + +
+

+ + + + + Library not found + + No methods found to benchmark + + +

+
+ + + + + +

+ + Remember to prefix the methods you want to benchmark with “bench”.
+ You might also want to overwrite Codebench->method_filter(). +
+

+ + + +
    + $benchmark) { ?> +
  • + +

    + + + +% + +

    + +
    + + + + + + + + + + + + $subject) { ?> + + + + + + + + +
    Benchmarks per subject for
    subject → returns
    + + [] → + + () + + + + + + + + + + + s + + +
    +
    + +
  • + +
+ + + + + + + + Raw output:', Debug::vars($codebench) ?> + + + + + + + diff --git a/modules/database/classes/Config/Database.php b/modules/database/classes/Config/Database.php new file mode 100644 index 0000000..7acb31b --- /dev/null +++ b/modules/database/classes/Config/Database.php @@ -0,0 +1,12 @@ +_db_instance = $config['instance']; + } + elseif ($this->_db_instance === NULL) + { + $this->_db_instance = Database::$default; + } + + if (isset($config['table_name'])) + { + $this->_table_name = $config['table_name']; + } + } + + /** + * Tries to load the specificed configuration group + * + * Returns FALSE if group does not exist or an array if it does + * + * @param string $group Configuration group + * @return boolean|array + */ + public function load($group) + { + /** + * Prevents the catch-22 scenario where the database config reader attempts to load the + * database connections details from the database. + * + * @link http://dev.kohanaframework.org/issues/4316 + */ + if ($group === 'database') + return FALSE; + + $query = DB::select('config_key', 'config_value') + ->from($this->_table_name) + ->where('group_name', '=', $group) + ->execute($this->_db_instance); + + return count($query) ? array_map('unserialize', $query->as_array('config_key', 'config_value')) : FALSE; + } +} diff --git a/modules/database/classes/Kohana/Config/Database/Writer.php b/modules/database/classes/Kohana/Config/Database/Writer.php new file mode 100644 index 0000000..f6b6738 --- /dev/null +++ b/modules/database/classes/Kohana/Config/Database/Writer.php @@ -0,0 +1,110 @@ +_loaded_keys[$group] = array_combine(array_keys($config), array_keys($config)); + } + + return $config; + } + + /** + * Writes the passed config for $group + * + * Returns chainable instance on success or throws + * Kohana_Config_Exception on failure + * + * @param string $group The config group + * @param string $key The config key to write to + * @param array $config The configuration to write + * @return boolean + */ + public function write($group, $key, $config) + { + $config = serialize($config); + + // Check to see if we've loaded the config from the table already + if (isset($this->_loaded_keys[$group][$key])) + { + $this->_update($group, $key, $config); + } + else + { + // Attempt to run an insert query + // This may fail if the config key already exists in the table + // and we don't know about it + try + { + $this->_insert($group, $key, $config); + } + catch (Database_Exception $e) + { + // Attempt to run an update instead + $this->_update($group, $key, $config); + } + } + + return TRUE; + } + + /** + * Insert the config values into the table + * + * @param string $group The config group + * @param string $key The config key to write to + * @param array $config The serialized configuration to write + * @return boolean + */ + protected function _insert($group, $key, $config) + { + DB::insert($this->_table_name, array('group_name', 'config_key', 'config_value')) + ->values(array($group, $key, $config)) + ->execute($this->_db_instance); + + return $this; + } + + /** + * Update the config values in the table + * + * @param string $group The config group + * @param string $key The config key to write to + * @param array $config The serialized configuration to write + * @return boolean + */ + protected function _update($group, $key, $config) + { + DB::update($this->_table_name) + ->set(array('config_value' => $config)) + ->where('group_name', '=', $group) + ->where('config_key', '=', $key) + ->execute($this->_db_instance); + + return $this; + } +} diff --git a/modules/database/classes/Kohana/DB.php b/modules/database/classes/Kohana/DB.php new file mode 100644 index 0000000..77388a9 --- /dev/null +++ b/modules/database/classes/Kohana/DB.php @@ -0,0 +1,139 @@ +[`DB::select_array()`](#select_array) | [Database_Query_Builder_Select] + * [`DB::update()`](#update) | [Database_Query_Builder_Update] + * [`DB::delete()`](#delete) | [Database_Query_Builder_Delete] + * [`DB::expr()`](#expr) | [Database_Expression] + * + * You pass the same parameters to these functions as you pass to the objects they return. + * + * @package Kohana/Database + * @category Base + * @author Kohana Team + * @copyright (c) 2009 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_DB { + + /** + * Create a new [Database_Query] of the given type. + * + * // Create a new SELECT query + * $query = DB::query(Database::SELECT, 'SELECT * FROM users'); + * + * // Create a new DELETE query + * $query = DB::query(Database::DELETE, 'DELETE FROM users WHERE id = 5'); + * + * Specifying the type changes the returned result. When using + * `Database::SELECT`, a [Database_Query_Result] will be returned. + * `Database::INSERT` queries will return the insert id and number of rows. + * For all other queries, the number of affected rows is returned. + * + * @param integer $type type: Database::SELECT, Database::UPDATE, etc + * @param string $sql SQL statement + * @return Database_Query + */ + public static function query($type, $sql) + { + return new Database_Query($type, $sql); + } + + /** + * Create a new [Database_Query_Builder_Select]. Each argument will be + * treated as a column. To generate a `foo AS bar` alias, use an array. + * + * // SELECT id, username + * $query = DB::select('id', 'username'); + * + * // SELECT id AS user_id + * $query = DB::select(array('id', 'user_id')); + * + * @param mixed $columns column name or array($column, $alias) or object + * @return Database_Query_Builder_Select + */ + public static function select($columns = NULL) + { + return new Database_Query_Builder_Select(func_get_args()); + } + + /** + * Create a new [Database_Query_Builder_Select] from an array of columns. + * + * // SELECT id, username + * $query = DB::select_array(array('id', 'username')); + * + * @param array $columns columns to select + * @return Database_Query_Builder_Select + */ + public static function select_array(array $columns = NULL) + { + return new Database_Query_Builder_Select($columns); + } + + /** + * Create a new [Database_Query_Builder_Insert]. + * + * // INSERT INTO users (id, username) + * $query = DB::insert('users', array('id', 'username')); + * + * @param string $table table to insert into + * @param array $columns list of column names or array($column, $alias) or object + * @return Database_Query_Builder_Insert + */ + public static function insert($table = NULL, array $columns = NULL) + { + return new Database_Query_Builder_Insert($table, $columns); + } + + /** + * Create a new [Database_Query_Builder_Update]. + * + * // UPDATE users + * $query = DB::update('users'); + * + * @param string $table table to update + * @return Database_Query_Builder_Update + */ + public static function update($table = NULL) + { + return new Database_Query_Builder_Update($table); + } + + /** + * Create a new [Database_Query_Builder_Delete]. + * + * // DELETE FROM users + * $query = DB::delete('users'); + * + * @param string $table table to delete from + * @return Database_Query_Builder_Delete + */ + public static function delete($table = NULL) + { + return new Database_Query_Builder_Delete($table); + } + + /** + * Create a new [Database_Expression] which is not escaped. An expression + * is the only way to use SQL functions within query builders. + * + * $expression = DB::expr('COUNT(users.id)'); + * $query = DB::update('users')->set(array('login_count' => DB::expr('login_count + 1')))->where('id', '=', $id); + * $users = ORM::factory('user')->where(DB::expr("BINARY `hash`"), '=', $hash)->find(); + * + * @param string $string expression + * @param array parameters + * @return Database_Expression + */ + public static function expr($string, $parameters = array()) + { + return new Database_Expression($string, $parameters); + } + +} // End DB diff --git a/modules/database/classes/Kohana/Database.php b/modules/database/classes/Kohana/Database.php new file mode 100644 index 0000000..267d938 --- /dev/null +++ b/modules/database/classes/Kohana/Database.php @@ -0,0 +1,726 @@ +load('database')->$name; + } + + if ( ! isset($config['type'])) + { + throw new Kohana_Exception('Database type not defined in :name configuration', + array(':name' => $name)); + } + + // Set the driver class name + $driver = 'Database_'.ucfirst($config['type']); + + // Create the database connection instance + $driver = new $driver($name, $config); + + // Store the database instance + Database::$instances[$name] = $driver; + } + + return Database::$instances[$name]; + } + + /** + * @var string the last query executed + */ + public $last_query; + + // Character that is used to quote identifiers + protected $_identifier = '"'; + + // Instance name + protected $_instance; + + // Raw server connection + protected $_connection; + + // Configuration array + protected $_config; + + /** + * Stores the database configuration locally and name the instance. + * + * [!!] This method cannot be accessed directly, you must use [Database::instance]. + * + * @return void + */ + public function __construct($name, array $config) + { + // Set the instance name + $this->_instance = $name; + + // Store the config locally + $this->_config = $config; + + if (empty($this->_config['table_prefix'])) + { + $this->_config['table_prefix'] = ''; + } + } + + /** + * Disconnect from the database when the object is destroyed. + * + * // Destroy the database instance + * unset(Database::instances[(string) $db], $db); + * + * [!!] Calling `unset($db)` is not enough to destroy the database, as it + * will still be stored in `Database::$instances`. + * + * @return void + */ + public function __destruct() + { + $this->disconnect(); + } + + /** + * Returns the database instance name. + * + * echo (string) $db; + * + * @return string + */ + public function __toString() + { + return $this->_instance; + } + + /** + * Connect to the database. This is called automatically when the first + * query is executed. + * + * $db->connect(); + * + * @throws Database_Exception + * @return void + */ + abstract public function connect(); + + /** + * Disconnect from the database. This is called automatically by [Database::__destruct]. + * Clears the database instance from [Database::$instances]. + * + * $db->disconnect(); + * + * @return boolean + */ + public function disconnect() + { + unset(Database::$instances[$this->_instance]); + + return TRUE; + } + + /** + * Set the connection character set. This is called automatically by [Database::connect]. + * + * $db->set_charset('utf8'); + * + * @throws Database_Exception + * @param string $charset character set name + * @return void + */ + abstract public function set_charset($charset); + + /** + * Perform an SQL query of the given type. + * + * // Make a SELECT query and use objects for results + * $db->query(Database::SELECT, 'SELECT * FROM groups', TRUE); + * + * // Make a SELECT query and use "Model_User" for the results + * $db->query(Database::SELECT, 'SELECT * FROM users LIMIT 1', 'Model_User'); + * + * @param integer $type Database::SELECT, Database::INSERT, etc + * @param string $sql SQL query + * @param mixed $as_object result object class string, TRUE for stdClass, FALSE for assoc array + * @param array $params object construct parameters for result class + * @return object Database_Result for SELECT queries + * @return array list (insert id, row count) for INSERT queries + * @return integer number of affected rows for all other queries + */ + abstract public function query($type, $sql, $as_object = FALSE, array $params = NULL); + + /** + * Start a SQL transaction + * + * // Start the transactions + * $db->begin(); + * + * try { + * DB::insert('users')->values($user1)... + * DB::insert('users')->values($user2)... + * // Insert successful commit the changes + * $db->commit(); + * } + * catch (Database_Exception $e) + * { + * // Insert failed. Rolling back changes... + * $db->rollback(); + * } + * + * @param string $mode transaction mode + * @return boolean + */ + abstract public function begin($mode = NULL); + + /** + * Commit the current transaction + * + * // Commit the database changes + * $db->commit(); + * + * @return boolean + */ + abstract public function commit(); + + /** + * Abort the current transaction + * + * // Undo the changes + * $db->rollback(); + * + * @return boolean + */ + abstract public function rollback(); + + /** + * Count the number of records in a table. + * + * // Get the total number of records in the "users" table + * $count = $db->count_records('users'); + * + * @param mixed $table table name string or array(query, alias) + * @return integer + */ + public function count_records($table) + { + // Quote the table name + $table = $this->quote_table($table); + + return $this->query(Database::SELECT, 'SELECT COUNT(*) AS total_row_count FROM '.$table, FALSE) + ->get('total_row_count'); + } + + /** + * Returns a normalized array describing the SQL data type + * + * $db->datatype('char'); + * + * @param string $type SQL data type + * @return array + */ + public function datatype($type) + { + static $types = array + ( + // SQL-92 + 'bit' => array('type' => 'string', 'exact' => TRUE), + 'bit varying' => array('type' => 'string'), + 'char' => array('type' => 'string', 'exact' => TRUE), + 'char varying' => array('type' => 'string'), + 'character' => array('type' => 'string', 'exact' => TRUE), + 'character varying' => array('type' => 'string'), + 'date' => array('type' => 'string'), + 'dec' => array('type' => 'float', 'exact' => TRUE), + 'decimal' => array('type' => 'float', 'exact' => TRUE), + 'double precision' => array('type' => 'float'), + 'float' => array('type' => 'float'), + 'int' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'), + 'integer' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'), + 'interval' => array('type' => 'string'), + 'national char' => array('type' => 'string', 'exact' => TRUE), + 'national char varying' => array('type' => 'string'), + 'national character' => array('type' => 'string', 'exact' => TRUE), + 'national character varying' => array('type' => 'string'), + 'nchar' => array('type' => 'string', 'exact' => TRUE), + 'nchar varying' => array('type' => 'string'), + 'numeric' => array('type' => 'float', 'exact' => TRUE), + 'real' => array('type' => 'float'), + 'smallint' => array('type' => 'int', 'min' => '-32768', 'max' => '32767'), + 'time' => array('type' => 'string'), + 'time with time zone' => array('type' => 'string'), + 'timestamp' => array('type' => 'string'), + 'timestamp with time zone' => array('type' => 'string'), + 'varchar' => array('type' => 'string'), + + // SQL:1999 + 'binary large object' => array('type' => 'string', 'binary' => TRUE), + 'blob' => array('type' => 'string', 'binary' => TRUE), + 'boolean' => array('type' => 'bool'), + 'char large object' => array('type' => 'string'), + 'character large object' => array('type' => 'string'), + 'clob' => array('type' => 'string'), + 'national character large object' => array('type' => 'string'), + 'nchar large object' => array('type' => 'string'), + 'nclob' => array('type' => 'string'), + 'time without time zone' => array('type' => 'string'), + 'timestamp without time zone' => array('type' => 'string'), + + // SQL:2003 + 'bigint' => array('type' => 'int', 'min' => '-9223372036854775808', 'max' => '9223372036854775807'), + + // SQL:2008 + 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE), + 'binary varying' => array('type' => 'string', 'binary' => TRUE), + 'varbinary' => array('type' => 'string', 'binary' => TRUE), + ); + + if (isset($types[$type])) + return $types[$type]; + + return array(); + } + + /** + * List all of the tables in the database. Optionally, a LIKE string can + * be used to search for specific tables. + * + * // Get all tables in the current database + * $tables = $db->list_tables(); + * + * // Get all user-related tables + * $tables = $db->list_tables('user%'); + * + * @param string $like table to search for + * @return array + */ + abstract public function list_tables($like = NULL); + + /** + * Lists all of the columns in a table. Optionally, a LIKE string can be + * used to search for specific fields. + * + * // Get all columns from the "users" table + * $columns = $db->list_columns('users'); + * + * // Get all name-related columns + * $columns = $db->list_columns('users', '%name%'); + * + * // Get the columns from a table that doesn't use the table prefix + * $columns = $db->list_columns('users', NULL, FALSE); + * + * @param string $table table to get columns from + * @param string $like column to search for + * @param boolean $add_prefix whether to add the table prefix automatically or not + * @return array + */ + abstract public function list_columns($table, $like = NULL, $add_prefix = TRUE); + + /** + * Extracts the text between parentheses, if any. + * + * // Returns: array('CHAR', '6') + * list($type, $length) = $db->_parse_type('CHAR(6)'); + * + * @param string $type + * @return array list containing the type and length, if any + */ + protected function _parse_type($type) + { + if (($open = strpos($type, '(')) === FALSE) + { + // No length specified + return array($type, NULL); + } + + // Closing parenthesis + $close = strrpos($type, ')', $open); + + // Length without parentheses + $length = substr($type, $open + 1, $close - 1 - $open); + + // Type without the length + $type = substr($type, 0, $open).substr($type, $close + 1); + + return array($type, $length); + } + + /** + * Return the table prefix defined in the current configuration. + * + * $prefix = $db->table_prefix(); + * + * @return string + */ + public function table_prefix() + { + return $this->_config['table_prefix']; + } + + /** + * Quote a value for an SQL query. + * + * $db->quote(NULL); // 'NULL' + * $db->quote(10); // 10 + * $db->quote('fred'); // 'fred' + * + * Objects passed to this function will be converted to strings. + * [Database_Expression] objects will be compiled. + * [Database_Query] objects will be compiled and converted to a sub-query. + * All other objects will be converted using the `__toString` method. + * + * @param mixed $value any value to quote + * @return string + * @uses Database::escape + */ + public function quote($value) + { + if ($value === NULL) + { + return 'NULL'; + } + elseif ($value === TRUE) + { + return "'1'"; + } + elseif ($value === FALSE) + { + return "'0'"; + } + elseif (is_object($value)) + { + if ($value instanceof Database_Query) + { + // Create a sub-query + return '('.$value->compile($this).')'; + } + elseif ($value instanceof Database_Expression) + { + // Compile the expression + return $value->compile($this); + } + else + { + // Convert the object to a string + return $this->quote( (string) $value); + } + } + elseif (is_array($value)) + { + return '('.implode(', ', array_map(array($this, __FUNCTION__), $value)).')'; + } + elseif (is_int($value)) + { + return (int) $value; + } + elseif (is_float($value)) + { + // Convert to non-locale aware float to prevent possible commas + return sprintf('%F', $value); + } + + return $this->escape($value); + } + + /** + * Quote a database column name and add the table prefix if needed. + * + * $column = $db->quote_column($column); + * + * You can also use SQL methods within identifiers. + * + * $column = $db->quote_column(DB::expr('COUNT(`column`)')); + * + * Objects passed to this function will be converted to strings. + * [Database_Expression] objects will be compiled. + * [Database_Query] objects will be compiled and converted to a sub-query. + * All other objects will be converted using the `__toString` method. + * + * @param mixed $column column name or array(column, alias) + * @return string + * @uses Database::quote_identifier + * @uses Database::table_prefix + */ + public function quote_column($column) + { + // Identifiers are escaped by repeating them + $escaped_identifier = $this->_identifier.$this->_identifier; + + if (is_array($column)) + { + list($column, $alias) = $column; + $alias = str_replace($this->_identifier, $escaped_identifier, $alias); + } + + if ($column instanceof Database_Query) + { + // Create a sub-query + $column = '('.$column->compile($this).')'; + } + elseif ($column instanceof Database_Expression) + { + // Compile the expression + $column = $column->compile($this); + } + else + { + // Convert to a string + $column = (string) $column; + + $column = str_replace($this->_identifier, $escaped_identifier, $column); + + if ($column === '*') + { + return $column; + } + elseif (strpos($column, '.') !== FALSE) + { + $parts = explode('.', $column); + + if ($prefix = $this->table_prefix()) + { + // Get the offset of the table name, 2nd-to-last part + $offset = count($parts) - 2; + + // Add the table prefix to the table name + $parts[$offset] = $prefix.$parts[$offset]; + } + + foreach ($parts as & $part) + { + if ($part !== '*') + { + // Quote each of the parts + $part = $this->_identifier.$part.$this->_identifier; + } + } + + $column = implode('.', $parts); + } + else + { + $column = $this->_identifier.$column.$this->_identifier; + } + } + + if (isset($alias)) + { + $column .= ' AS '.$this->_identifier.$alias.$this->_identifier; + } + + return $column; + } + + /** + * Quote a database table name and adds the table prefix if needed. + * + * $table = $db->quote_table($table); + * + * Objects passed to this function will be converted to strings. + * [Database_Expression] objects will be compiled. + * [Database_Query] objects will be compiled and converted to a sub-query. + * All other objects will be converted using the `__toString` method. + * + * @param mixed $table table name or array(table, alias) + * @return string + * @uses Database::quote_identifier + * @uses Database::table_prefix + */ + public function quote_table($table) + { + // Identifiers are escaped by repeating them + $escaped_identifier = $this->_identifier.$this->_identifier; + + if (is_array($table)) + { + list($table, $alias) = $table; + $alias = str_replace($this->_identifier, $escaped_identifier, $alias); + } + + if ($table instanceof Database_Query) + { + // Create a sub-query + $table = '('.$table->compile($this).')'; + } + elseif ($table instanceof Database_Expression) + { + // Compile the expression + $table = $table->compile($this); + } + else + { + // Convert to a string + $table = (string) $table; + + $table = str_replace($this->_identifier, $escaped_identifier, $table); + + if (strpos($table, '.') !== FALSE) + { + $parts = explode('.', $table); + + if ($prefix = $this->table_prefix()) + { + // Get the offset of the table name, last part + $offset = count($parts) - 1; + + // Add the table prefix to the table name + $parts[$offset] = $prefix.$parts[$offset]; + } + + foreach ($parts as & $part) + { + // Quote each of the parts + $part = $this->_identifier.$part.$this->_identifier; + } + + $table = implode('.', $parts); + } + else + { + // Add the table prefix + $table = $this->_identifier.$this->table_prefix().$table.$this->_identifier; + } + } + + if (isset($alias)) + { + // Attach table prefix to alias + $table .= ' AS '.$this->_identifier.$this->table_prefix().$alias.$this->_identifier; + } + + return $table; + } + + /** + * Quote a database identifier + * + * Objects passed to this function will be converted to strings. + * [Database_Expression] objects will be compiled. + * [Database_Query] objects will be compiled and converted to a sub-query. + * All other objects will be converted using the `__toString` method. + * + * @param mixed $value any identifier + * @return string + */ + public function quote_identifier($value) + { + // Identifiers are escaped by repeating them + $escaped_identifier = $this->_identifier.$this->_identifier; + + if (is_array($value)) + { + list($value, $alias) = $value; + $alias = str_replace($this->_identifier, $escaped_identifier, $alias); + } + + if ($value instanceof Database_Query) + { + // Create a sub-query + $value = '('.$value->compile($this).')'; + } + elseif ($value instanceof Database_Expression) + { + // Compile the expression + $value = $value->compile($this); + } + else + { + // Convert to a string + $value = (string) $value; + + $value = str_replace($this->_identifier, $escaped_identifier, $value); + + if (strpos($value, '.') !== FALSE) + { + $parts = explode('.', $value); + + foreach ($parts as & $part) + { + // Quote each of the parts + $part = $this->_identifier.$part.$this->_identifier; + } + + $value = implode('.', $parts); + } + else + { + $value = $this->_identifier.$value.$this->_identifier; + } + } + + if (isset($alias)) + { + $value .= ' AS '.$this->_identifier.$alias.$this->_identifier; + } + + return $value; + } + + /** + * Sanitize a string by escaping characters that could cause an SQL + * injection attack. + * + * $value = $db->escape('any string'); + * + * @param string $value value to quote + * @return string + */ + abstract public function escape($value); + +} // End Database_Connection diff --git a/modules/database/classes/Kohana/Database/Exception.php b/modules/database/classes/Kohana/Database/Exception.php new file mode 100644 index 0000000..68f709e --- /dev/null +++ b/modules/database/classes/Kohana/Database/Exception.php @@ -0,0 +1,11 @@ +_value = $value; + $this->_parameters = $parameters; + } + + /** + * Bind a variable to a parameter. + * + * @param string $param parameter key to replace + * @param mixed $var variable to use + * @return $this + */ + public function bind($param, & $var) + { + $this->_parameters[$param] =& $var; + + return $this; + } + + /** + * Set the value of a parameter. + * + * @param string $param parameter key to replace + * @param mixed $value value to use + * @return $this + */ + public function param($param, $value) + { + $this->_parameters[$param] = $value; + + return $this; + } + + /** + * Add multiple parameter values. + * + * @param array $params list of parameter values + * @return $this + */ + public function parameters(array $params) + { + $this->_parameters = $params + $this->_parameters; + + return $this; + } + + /** + * Get the expression value as a string. + * + * $sql = $expression->value(); + * + * @return string + */ + public function value() + { + return (string) $this->_value; + } + + /** + * Return the value of the expression as a string. + * + * echo $expression; + * + * @return string + * @uses Database_Expression::value + */ + public function __toString() + { + return $this->value(); + } + + /** + * Compile the SQL expression and return it. Replaces any parameters with + * their given values. + * + * @param mixed Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + $value = $this->value(); + + if ( ! empty($this->_parameters)) + { + // Quote all of the parameter values + $params = array_map(array($db, 'quote'), $this->_parameters); + + // Replace the values in the expression + $value = strtr($value, $params); + } + + return $value; + } + +} // End Database_Expression diff --git a/modules/database/classes/Kohana/Database/MySQL.php b/modules/database/classes/Kohana/Database/MySQL.php new file mode 100644 index 0000000..8700904 --- /dev/null +++ b/modules/database/classes/Kohana/Database/MySQL.php @@ -0,0 +1,443 @@ +_connection) + return; + + if (Database_MySQL::$_set_names === NULL) + { + // Determine if we can use mysql_set_charset(), which is only + // available on PHP 5.2.3+ when compiled against MySQL 5.0+ + Database_MySQL::$_set_names = ! function_exists('mysql_set_charset'); + } + + // Extract the connection parameters, adding required variabels + extract($this->_config['connection'] + array( + 'database' => '', + 'hostname' => '', + 'username' => '', + 'password' => '', + 'persistent' => FALSE, + )); + + // Prevent this information from showing up in traces + unset($this->_config['connection']['username'], $this->_config['connection']['password']); + + try + { + if ($persistent) + { + // Create a persistent connection + $this->_connection = mysql_pconnect($hostname, $username, $password); + } + else + { + // Create a connection and force it to be a new link + $this->_connection = mysql_connect($hostname, $username, $password, TRUE); + } + } + catch (Exception $e) + { + // No connection exists + $this->_connection = NULL; + + throw new Database_Exception(':error', + array(':error' => $e->getMessage()), + $e->getCode()); + } + + // \xFF is a better delimiter, but the PHP driver uses underscore + $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password); + + $this->_select_db($database); + + if ( ! empty($this->_config['charset'])) + { + // Set the character set + $this->set_charset($this->_config['charset']); + } + + if ( ! empty($this->_config['connection']['variables'])) + { + // Set session variables + $variables = array(); + + foreach ($this->_config['connection']['variables'] as $var => $val) + { + $variables[] = 'SESSION '.$var.' = '.$this->quote($val); + } + + mysql_query('SET '.implode(', ', $variables), $this->_connection); + } + } + + /** + * Select the database + * + * @param string $database Database + * @return void + */ + protected function _select_db($database) + { + if ( ! mysql_select_db($database, $this->_connection)) + { + // Unable to select database + throw new Database_Exception(':error', + array(':error' => mysql_error($this->_connection)), + mysql_errno($this->_connection)); + } + + Database_MySQL::$_current_databases[$this->_connection_id] = $database; + } + + public function disconnect() + { + try + { + // Database is assumed disconnected + $status = TRUE; + + if (is_resource($this->_connection)) + { + if ($status = mysql_close($this->_connection)) + { + // Clear the connection + $this->_connection = NULL; + + // Clear the instance + parent::disconnect(); + } + } + } + catch (Exception $e) + { + // Database is probably not disconnected + $status = ! is_resource($this->_connection); + } + + return $status; + } + + public function set_charset($charset) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + if (Database_MySQL::$_set_names === TRUE) + { + // PHP is compiled against MySQL 4.x + $status = (bool) mysql_query('SET NAMES '.$this->quote($charset), $this->_connection); + } + else + { + // PHP is compiled against MySQL 5.x + $status = mysql_set_charset($charset, $this->_connection); + } + + if ($status === FALSE) + { + throw new Database_Exception(':error', + array(':error' => mysql_error($this->_connection)), + mysql_errno($this->_connection)); + } + } + + public function query($type, $sql, $as_object = FALSE, array $params = NULL) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + if (Kohana::$profiling) + { + // Benchmark this query for the current instance + $benchmark = Profiler::start("Database ({$this->_instance})", $sql); + } + + if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== Database_MySQL::$_current_databases[$this->_connection_id]) + { + // Select database on persistent connections + $this->_select_db($this->_config['connection']['database']); + } + + // Execute the query + if (($result = mysql_query($sql, $this->_connection)) === FALSE) + { + if (isset($benchmark)) + { + // This benchmark is worthless + Profiler::delete($benchmark); + } + + throw new Database_Exception(':error [ :query ]', + array(':error' => mysql_error($this->_connection), ':query' => $sql), + mysql_errno($this->_connection)); + } + + if (isset($benchmark)) + { + Profiler::stop($benchmark); + } + + // Set the last query + $this->last_query = $sql; + + if ($type === Database::SELECT) + { + // Return an iterator of results + return new Database_MySQL_Result($result, $sql, $as_object, $params); + } + elseif ($type === Database::INSERT) + { + // Return a list of insert id and rows created + return array( + mysql_insert_id($this->_connection), + mysql_affected_rows($this->_connection), + ); + } + else + { + // Return the number of rows affected + return mysql_affected_rows($this->_connection); + } + } + + public function datatype($type) + { + static $types = array + ( + 'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'), + 'bool' => array('type' => 'bool'), + 'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'), + 'datetime' => array('type' => 'string'), + 'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'), + 'double' => array('type' => 'float'), + 'double precision unsigned' => array('type' => 'float', 'min' => '0'), + 'double unsigned' => array('type' => 'float', 'min' => '0'), + 'enum' => array('type' => 'string'), + 'fixed' => array('type' => 'float', 'exact' => TRUE), + 'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'), + 'float unsigned' => array('type' => 'float', 'min' => '0'), + 'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'), + 'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'), + 'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'), + 'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'), + 'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'), + 'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'), + 'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'), + 'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'), + 'national varchar' => array('type' => 'string'), + 'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'), + 'nvarchar' => array('type' => 'string'), + 'point' => array('type' => 'string', 'binary' => TRUE), + 'real unsigned' => array('type' => 'float', 'min' => '0'), + 'set' => array('type' => 'string'), + 'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'), + 'text' => array('type' => 'string', 'character_maximum_length' => '65535'), + 'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'), + 'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'), + 'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'), + 'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'), + 'year' => array('type' => 'string'), + ); + + $type = str_replace(' zerofill', '', $type); + + if (isset($types[$type])) + return $types[$type]; + + return parent::datatype($type); + } + + /** + * Start a SQL transaction + * + * @link http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html + * + * @param string $mode Isolation level + * @return boolean + */ + public function begin($mode = NULL) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + if ($mode AND ! mysql_query("SET TRANSACTION ISOLATION LEVEL $mode", $this->_connection)) + { + throw new Database_Exception(':error', + array(':error' => mysql_error($this->_connection)), + mysql_errno($this->_connection)); + } + + return (bool) mysql_query('START TRANSACTION', $this->_connection); + } + + /** + * Commit a SQL transaction + * + * @return boolean + */ + public function commit() + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + return (bool) mysql_query('COMMIT', $this->_connection); + } + + /** + * Rollback a SQL transaction + * + * @return boolean + */ + public function rollback() + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + return (bool) mysql_query('ROLLBACK', $this->_connection); + } + + public function list_tables($like = NULL) + { + if (is_string($like)) + { + // Search for table names + $result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE); + } + else + { + // Find all table names + $result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE); + } + + $tables = array(); + foreach ($result as $row) + { + $tables[] = reset($row); + } + + return $tables; + } + + public function list_columns($table, $like = NULL, $add_prefix = TRUE) + { + // Quote the table name + $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table; + + if (is_string($like)) + { + // Search for column names + $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE); + } + else + { + // Find all column names + $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE); + } + + $count = 0; + $columns = array(); + foreach ($result as $row) + { + list($type, $length) = $this->_parse_type($row['Type']); + + $column = $this->datatype($type); + + $column['column_name'] = $row['Field']; + $column['column_default'] = $row['Default']; + $column['data_type'] = $type; + $column['is_nullable'] = ($row['Null'] == 'YES'); + $column['ordinal_position'] = ++$count; + + switch ($column['type']) + { + case 'float': + if (isset($length)) + { + list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length); + } + break; + case 'int': + if (isset($length)) + { + // MySQL attribute + $column['display'] = $length; + } + break; + case 'string': + switch ($column['data_type']) + { + case 'binary': + case 'varbinary': + $column['character_maximum_length'] = $length; + break; + case 'char': + case 'varchar': + $column['character_maximum_length'] = $length; + case 'text': + case 'tinytext': + case 'mediumtext': + case 'longtext': + $column['collation_name'] = $row['Collation']; + break; + case 'enum': + case 'set': + $column['collation_name'] = $row['Collation']; + $column['options'] = explode('\',\'', substr($length, 1, -1)); + break; + } + break; + } + + // MySQL attributes + $column['comment'] = $row['Comment']; + $column['extra'] = $row['Extra']; + $column['key'] = $row['Key']; + $column['privileges'] = $row['Privileges']; + + $columns[$row['Field']] = $column; + } + + return $columns; + } + + public function escape($value) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + if (($value = mysql_real_escape_string( (string) $value, $this->_connection)) === FALSE) + { + throw new Database_Exception(':error', + array(':error' => mysql_error($this->_connection)), + mysql_errno($this->_connection)); + } + + // SQL standard is to use single-quotes for all values + return "'$value'"; + } + +} // End Database_MySQL diff --git a/modules/database/classes/Kohana/Database/MySQL/Result.php b/modules/database/classes/Kohana/Database/MySQL/Result.php new file mode 100644 index 0000000..22a5d14 --- /dev/null +++ b/modules/database/classes/Kohana/Database/MySQL/Result.php @@ -0,0 +1,71 @@ +_total_rows = mysql_num_rows($result); + } + + public function __destruct() + { + if (is_resource($this->_result)) + { + mysql_free_result($this->_result); + } + } + + public function seek($offset) + { + if ($this->offsetExists($offset) AND mysql_data_seek($this->_result, $offset)) + { + // Set the current row to the offset + $this->_current_row = $this->_internal_row = $offset; + + return TRUE; + } + else + { + return FALSE; + } + } + + public function current() + { + if ($this->_current_row !== $this->_internal_row AND ! $this->seek($this->_current_row)) + return NULL; + + // Increment internal row for optimization assuming rows are fetched in order + $this->_internal_row++; + + if ($this->_as_object === TRUE) + { + // Return an stdClass + return mysql_fetch_object($this->_result); + } + elseif (is_string($this->_as_object)) + { + // Return an object of given class name + return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params); + } + else + { + // Return an array of the row + return mysql_fetch_assoc($this->_result); + } + } + +} // End Database_MySQL_Result_Select diff --git a/modules/database/classes/Kohana/Database/PDO.php b/modules/database/classes/Kohana/Database/PDO.php new file mode 100644 index 0000000..573f8d3 --- /dev/null +++ b/modules/database/classes/Kohana/Database/PDO.php @@ -0,0 +1,247 @@ +_config['identifier'])) + { + // Allow the identifier to be overloaded per-connection + $this->_identifier = (string) $this->_config['identifier']; + } + } + + public function connect() + { + if ($this->_connection) + return; + + // Extract the connection parameters, adding required variabels + extract($this->_config['connection'] + array( + 'dsn' => '', + 'username' => NULL, + 'password' => NULL, + 'persistent' => FALSE, + )); + + // Clear the connection parameters for security + unset($this->_config['connection']); + + // Force PDO to use exceptions for all errors + $options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; + + if ( ! empty($persistent)) + { + // Make the connection persistent + $options[PDO::ATTR_PERSISTENT] = TRUE; + } + + try + { + // Create a new PDO connection + $this->_connection = new PDO($dsn, $username, $password, $options); + } + catch (PDOException $e) + { + throw new Database_Exception(':error', + array(':error' => $e->getMessage()), + $e->getCode()); + } + } + + /** + * Create or redefine a SQL aggregate function. + * + * [!!] Works only with SQLite + * + * @link http://php.net/manual/function.pdo-sqlitecreateaggregate + * + * @param string $name Name of the SQL function to be created or redefined + * @param callback $step Called for each row of a result set + * @param callback $final Called after all rows of a result set have been processed + * @param integer $arguments Number of arguments that the SQL function takes + * + * @return boolean + */ + public function create_aggregate($name, $step, $final, $arguments = -1) + { + $this->_connection or $this->connect(); + + return $this->_connection->sqliteCreateAggregate( + $name, $step, $final, $arguments + ); + } + + /** + * Create or redefine a SQL function. + * + * [!!] Works only with SQLite + * + * @link http://php.net/manual/function.pdo-sqlitecreatefunction + * + * @param string $name Name of the SQL function to be created or redefined + * @param callback $callback Callback which implements the SQL function + * @param integer $arguments Number of arguments that the SQL function takes + * + * @return boolean + */ + public function create_function($name, $callback, $arguments = -1) + { + $this->_connection or $this->connect(); + + return $this->_connection->sqliteCreateFunction( + $name, $callback, $arguments + ); + } + + public function disconnect() + { + // Destroy the PDO object + $this->_connection = NULL; + + return parent::disconnect(); + } + + public function set_charset($charset) + { + // Make sure the database is connected + $this->_connection OR $this->connect(); + + // This SQL-92 syntax is not supported by all drivers + $this->_connection->exec('SET NAMES '.$this->quote($charset)); + } + + public function query($type, $sql, $as_object = FALSE, array $params = NULL) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + if (Kohana::$profiling) + { + // Benchmark this query for the current instance + $benchmark = Profiler::start("Database ({$this->_instance})", $sql); + } + + try + { + $result = $this->_connection->query($sql); + } + catch (Exception $e) + { + if (isset($benchmark)) + { + // This benchmark is worthless + Profiler::delete($benchmark); + } + + // Convert the exception in a database exception + throw new Database_Exception(':error [ :query ]', + array( + ':error' => $e->getMessage(), + ':query' => $sql + ), + $e->getCode()); + } + + if (isset($benchmark)) + { + Profiler::stop($benchmark); + } + + // Set the last query + $this->last_query = $sql; + + if ($type === Database::SELECT) + { + // Convert the result into an array, as PDOStatement::rowCount is not reliable + if ($as_object === FALSE) + { + $result->setFetchMode(PDO::FETCH_ASSOC); + } + elseif (is_string($as_object)) + { + $result->setFetchMode(PDO::FETCH_CLASS, $as_object, $params); + } + else + { + $result->setFetchMode(PDO::FETCH_CLASS, 'stdClass'); + } + + $result = $result->fetchAll(); + + // Return an iterator of results + return new Database_Result_Cached($result, $sql, $as_object, $params); + } + elseif ($type === Database::INSERT) + { + // Return a list of insert id and rows created + return array( + $this->_connection->lastInsertId(), + $result->rowCount(), + ); + } + else + { + // Return the number of rows affected + return $result->rowCount(); + } + } + + public function begin($mode = NULL) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + return $this->_connection->beginTransaction(); + } + + public function commit() + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + return $this->_connection->commit(); + } + + public function rollback() + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + return $this->_connection->rollBack(); + } + + public function list_tables($like = NULL) + { + throw new Kohana_Exception('Database method :method is not supported by :class', + array(':method' => __FUNCTION__, ':class' => __CLASS__)); + } + + public function list_columns($table, $like = NULL, $add_prefix = TRUE) + { + throw new Kohana_Exception('Database method :method is not supported by :class', + array(':method' => __FUNCTION__, ':class' => __CLASS__)); + } + + public function escape($value) + { + // Make sure the database is connected + $this->_connection or $this->connect(); + + return $this->_connection->quote($value); + } + +} // End Database_PDO diff --git a/modules/database/classes/Kohana/Database/Query.php b/modules/database/classes/Kohana/Database/Query.php new file mode 100644 index 0000000..480e41b --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query.php @@ -0,0 +1,262 @@ +_type = $type; + $this->_sql = $sql; + } + + /** + * Return the SQL query string. + * + * @return string + */ + public function __toString() + { + try + { + // Return the SQL string + return $this->compile(Database::instance()); + } + catch (Exception $e) + { + return Kohana_Exception::text($e); + } + } + + /** + * Get the type of the query. + * + * @return integer + */ + public function type() + { + return $this->_type; + } + + /** + * Enables the query to be cached for a specified amount of time. + * + * @param integer $lifetime number of seconds to cache, 0 deletes it from the cache + * @param boolean whether or not to execute the query during a cache hit + * @return $this + * @uses Kohana::$cache_life + */ + public function cached($lifetime = NULL, $force = FALSE) + { + if ($lifetime === NULL) + { + // Use the global setting + $lifetime = Kohana::$cache_life; + } + + $this->_force_execute = $force; + $this->_lifetime = $lifetime; + + return $this; + } + + /** + * Returns results as associative arrays + * + * @return $this + */ + public function as_assoc() + { + $this->_as_object = FALSE; + + $this->_object_params = array(); + + return $this; + } + + /** + * Returns results as objects + * + * @param string $class classname or TRUE for stdClass + * @param array $params + * @return $this + */ + public function as_object($class = TRUE, array $params = NULL) + { + $this->_as_object = $class; + + if ($params) + { + // Add object parameters + $this->_object_params = $params; + } + + return $this; + } + + /** + * Set the value of a parameter in the query. + * + * @param string $param parameter key to replace + * @param mixed $value value to use + * @return $this + */ + public function param($param, $value) + { + // Add or overload a new parameter + $this->_parameters[$param] = $value; + + return $this; + } + + /** + * Bind a variable to a parameter in the query. + * + * @param string $param parameter key to replace + * @param mixed $var variable to use + * @return $this + */ + public function bind($param, & $var) + { + // Bind a value to a variable + $this->_parameters[$param] =& $var; + + return $this; + } + + /** + * Add multiple parameters to the query. + * + * @param array $params list of parameters + * @return $this + */ + public function parameters(array $params) + { + // Merge the new parameters in + $this->_parameters = $params + $this->_parameters; + + return $this; + } + + /** + * Compile the SQL query and return it. Replaces any parameters with their + * given values. + * + * @param mixed $db Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + // Import the SQL locally + $sql = $this->_sql; + + if ( ! empty($this->_parameters)) + { + // Quote all of the values + $values = array_map(array($db, 'quote'), $this->_parameters); + + // Replace the values in the SQL + $sql = strtr($sql, $values); + } + + return $sql; + } + + /** + * Execute the current query on the given database. + * + * @param mixed $db Database instance or name of instance + * @param string result object classname, TRUE for stdClass or FALSE for array + * @param array result object constructor arguments + * @return object Database_Result for SELECT queries + * @return mixed the insert id for INSERT queries + * @return integer number of affected rows for all other queries + */ + public function execute($db = NULL, $as_object = NULL, $object_params = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + if ($as_object === NULL) + { + $as_object = $this->_as_object; + } + + if ($object_params === NULL) + { + $object_params = $this->_object_params; + } + + // Compile the SQL query + $sql = $this->compile($db); + + if ($this->_lifetime !== NULL AND $this->_type === Database::SELECT) + { + // Set the cache key based on the database instance name and SQL + $cache_key = 'Database::query("'.$db.'", "'.$sql.'")'; + + // Read the cache first to delete a possible hit with lifetime <= 0 + if (($result = Kohana::cache($cache_key, NULL, $this->_lifetime)) !== NULL + AND ! $this->_force_execute) + { + // Return a cached result + return new Database_Result_Cached($result, $sql, $as_object, $object_params); + } + } + + // Execute the query + $result = $db->query($this->_type, $sql, $as_object, $object_params); + + if (isset($cache_key) AND $this->_lifetime > 0) + { + // Cache the result array + Kohana::cache($cache_key, $result->as_array(), $this->_lifetime); + } + + return $result; + } + +} // End Database_Query diff --git a/modules/database/classes/Kohana/Database/Query/Builder.php b/modules/database/classes/Kohana/Database/Query/Builder.php new file mode 100644 index 0000000..3e1bdb5 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder.php @@ -0,0 +1,248 @@ +compile($db); + } + + return implode(' ', $statements); + } + + /** + * Compiles an array of conditions into an SQL partial. Used for WHERE + * and HAVING. + * + * @param object $db Database instance + * @param array $conditions condition statements + * @return string + */ + protected function _compile_conditions(Database $db, array $conditions) + { + $last_condition = NULL; + + $sql = ''; + foreach ($conditions as $group) + { + // Process groups of conditions + foreach ($group as $logic => $condition) + { + if ($condition === '(') + { + if ( ! empty($sql) AND $last_condition !== '(') + { + // Include logic operator + $sql .= ' '.$logic.' '; + } + + $sql .= '('; + } + elseif ($condition === ')') + { + $sql .= ')'; + } + else + { + if ( ! empty($sql) AND $last_condition !== '(') + { + // Add the logic operator + $sql .= ' '.$logic.' '; + } + + // Split the condition + list($column, $op, $value) = $condition; + + if ($value === NULL) + { + if ($op === '=') + { + // Convert "val = NULL" to "val IS NULL" + $op = 'IS'; + } + elseif ($op === '!=') + { + // Convert "val != NULL" to "valu IS NOT NULL" + $op = 'IS NOT'; + } + } + + // Database operators are always uppercase + $op = strtoupper($op); + + if ($op === 'BETWEEN' AND is_array($value)) + { + // BETWEEN always has exactly two arguments + list($min, $max) = $value; + + if ((is_string($min) AND array_key_exists($min, $this->_parameters)) === FALSE) + { + // Quote the value, it is not a parameter + $min = $db->quote($min); + } + + if ((is_string($max) AND array_key_exists($max, $this->_parameters)) === FALSE) + { + // Quote the value, it is not a parameter + $max = $db->quote($max); + } + + // Quote the min and max value + $value = $min.' AND '.$max; + } + elseif ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE) + { + // Quote the value, it is not a parameter + $value = $db->quote($value); + } + + if ($column) + { + if (is_array($column)) + { + // Use the column name + $column = $db->quote_identifier(reset($column)); + } + else + { + // Apply proper quoting to the column + $column = $db->quote_column($column); + } + } + + // Append the statement to the query + $sql .= trim($column.' '.$op.' '.$value); + } + + $last_condition = $condition; + } + } + + return $sql; + } + + /** + * Compiles an array of set values into an SQL partial. Used for UPDATE. + * + * @param object $db Database instance + * @param array $values updated values + * @return string + */ + protected function _compile_set(Database $db, array $values) + { + $set = array(); + foreach ($values as $group) + { + // Split the set + list ($column, $value) = $group; + + // Quote the column name + $column = $db->quote_column($column); + + if ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE) + { + // Quote the value, it is not a parameter + $value = $db->quote($value); + } + + $set[$column] = $column.' = '.$value; + } + + return implode(', ', $set); + } + + /** + * Compiles an array of GROUP BY columns into an SQL partial. + * + * @param object $db Database instance + * @param array $columns + * @return string + */ + protected function _compile_group_by(Database $db, array $columns) + { + $group = array(); + + foreach ($columns as $column) + { + if (is_array($column)) + { + // Use the column alias + $column = $db->quote_identifier(end($column)); + } + else + { + // Apply proper quoting to the column + $column = $db->quote_column($column); + } + + $group[] = $column; + } + + return 'GROUP BY '.implode(', ', $group); + } + + /** + * Compiles an array of ORDER BY statements into an SQL partial. + * + * @param object $db Database instance + * @param array $columns sorting columns + * @return string + */ + protected function _compile_order_by(Database $db, array $columns) + { + $sort = array(); + foreach ($columns as $group) + { + list ($column, $direction) = $group; + + if (is_array($column)) + { + // Use the column alias + $column = $db->quote_identifier(end($column)); + } + else + { + // Apply proper quoting to the column + $column = $db->quote_column($column); + } + + if ($direction) + { + // Make the direction uppercase + $direction = ' '.strtoupper($direction); + } + + $sort[] = $column.$direction; + } + + return 'ORDER BY '.implode(', ', $sort); + } + + /** + * Reset the current builder status. + * + * @return $this + */ + abstract public function reset(); + +} // End Database_Query_Builder diff --git a/modules/database/classes/Kohana/Database/Query/Builder/Delete.php b/modules/database/classes/Kohana/Database/Query/Builder/Delete.php new file mode 100644 index 0000000..0bc4761 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder/Delete.php @@ -0,0 +1,99 @@ +_table = $table; + } + + // Start the query with no SQL + return parent::__construct(Database::DELETE, ''); + } + + /** + * Sets the table to delete from. + * + * @param mixed $table table name or array($table, $alias) or object + * @return $this + */ + public function table($table) + { + $this->_table = $table; + + return $this; + } + + /** + * Compile the SQL query and return it. + * + * @param mixed $db Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + // Start a deletion query + $query = 'DELETE FROM '.$db->quote_table($this->_table); + + if ( ! empty($this->_where)) + { + // Add deletion conditions + $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where); + } + + if ( ! empty($this->_order_by)) + { + // Add sorting + $query .= ' '.$this->_compile_order_by($db, $this->_order_by); + } + + if ($this->_limit !== NULL) + { + // Add limiting + $query .= ' LIMIT '.$this->_limit; + } + + $this->_sql = $query; + + return parent::compile($db); + } + + public function reset() + { + $this->_table = NULL; + $this->_where = array(); + + $this->_parameters = array(); + + $this->_sql = NULL; + + return $this; + } + +} // End Database_Query_Builder_Delete diff --git a/modules/database/classes/Kohana/Database/Query/Builder/Insert.php b/modules/database/classes/Kohana/Database/Query/Builder/Insert.php new file mode 100644 index 0000000..aa3c807 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder/Insert.php @@ -0,0 +1,181 @@ +_table = $table; + } + + if ($columns) + { + // Set the column names + $this->_columns = $columns; + } + + // Start the query with no SQL + return parent::__construct(Database::INSERT, ''); + } + + /** + * Sets the table to insert into. + * + * @param mixed $table table name or array($table, $alias) or object + * @return $this + */ + public function table($table) + { + $this->_table = $table; + + return $this; + } + + /** + * Set the columns that will be inserted. + * + * @param array $columns column names + * @return $this + */ + public function columns(array $columns) + { + $this->_columns = $columns; + + return $this; + } + + /** + * Adds or overwrites values. Multiple value sets can be added. + * + * @param array $values values list + * @param ... + * @return $this + */ + public function values(array $values) + { + if ( ! is_array($this->_values)) + { + throw new Kohana_Exception('INSERT INTO ... SELECT statements cannot be combined with INSERT INTO ... VALUES'); + } + + // Get all of the passed values + $values = func_get_args(); + + $this->_values = array_merge($this->_values, $values); + + return $this; + } + + /** + * Use a sub-query to for the inserted values. + * + * @param object $query Database_Query of SELECT type + * @return $this + */ + public function select(Database_Query $query) + { + if ($query->type() !== Database::SELECT) + { + throw new Kohana_Exception('Only SELECT queries can be combined with INSERT queries'); + } + + $this->_values = $query; + + return $this; + } + + /** + * Compile the SQL query and return it. + * + * @param mixed $db Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + // Start an insertion query + $query = 'INSERT INTO '.$db->quote_table($this->_table); + + // Add the column names + $query .= ' ('.implode(', ', array_map(array($db, 'quote_column'), $this->_columns)).') '; + + if (is_array($this->_values)) + { + // Callback for quoting values + $quote = array($db, 'quote'); + + $groups = array(); + foreach ($this->_values as $group) + { + foreach ($group as $offset => $value) + { + if ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE) + { + // Quote the value, it is not a parameter + $group[$offset] = $db->quote($value); + } + } + + $groups[] = '('.implode(', ', $group).')'; + } + + // Add the values + $query .= 'VALUES '.implode(', ', $groups); + } + else + { + // Add the sub-query + $query .= (string) $this->_values; + } + + $this->_sql = $query; + + return parent::compile($db);; + } + + public function reset() + { + $this->_table = NULL; + + $this->_columns = + $this->_values = array(); + + $this->_parameters = array(); + + $this->_sql = NULL; + + return $this; + } + +} // End Database_Query_Builder_Insert diff --git a/modules/database/classes/Kohana/Database/Query/Builder/Join.php b/modules/database/classes/Kohana/Database/Query/Builder/Join.php new file mode 100644 index 0000000..10402f6 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder/Join.php @@ -0,0 +1,149 @@ +_table = $table; + + if ($type !== NULL) + { + // Set the JOIN type + $this->_type = (string) $type; + } + } + + /** + * Adds a new condition for joining. + * + * @param mixed $c1 column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $c2 column name or array($column, $alias) or object + * @return $this + */ + public function on($c1, $op, $c2) + { + if ( ! empty($this->_using)) + { + throw new Kohana_Exception('JOIN ... ON ... cannot be combined with JOIN ... USING ...'); + } + + $this->_on[] = array($c1, $op, $c2); + + return $this; + } + + /** + * Adds a new condition for joining. + * + * @param string $columns column name + * @return $this + */ + public function using($columns) + { + if ( ! empty($this->_on)) + { + throw new Kohana_Exception('JOIN ... ON ... cannot be combined with JOIN ... USING ...'); + } + + $columns = func_get_args(); + + $this->_using = array_merge($this->_using, $columns); + + return $this; + } + + /** + * Compile the SQL partial for a JOIN statement and return it. + * + * @param mixed $db Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + if ($this->_type) + { + $sql = strtoupper($this->_type).' JOIN'; + } + else + { + $sql = 'JOIN'; + } + + // Quote the table name that is being joined + $sql .= ' '.$db->quote_table($this->_table); + + if ( ! empty($this->_using)) + { + // Quote and concat the columns + $sql .= ' USING ('.implode(', ', array_map(array($db, 'quote_column'), $this->_using)).')'; + } + else + { + $conditions = array(); + foreach ($this->_on as $condition) + { + // Split the condition + list($c1, $op, $c2) = $condition; + + if ($op) + { + // Make the operator uppercase and spaced + $op = ' '.strtoupper($op); + } + + // Quote each of the columns used for the condition + $conditions[] = $db->quote_column($c1).$op.' '.$db->quote_column($c2); + } + + // Concat the conditions "... AND ..." + $sql .= ' ON ('.implode(' AND ', $conditions).')'; + } + + return $sql; + } + + public function reset() + { + $this->_type = + $this->_table = NULL; + + $this->_on = array(); + } + +} // End Database_Query_Builder_Join diff --git a/modules/database/classes/Kohana/Database/Query/Builder/Select.php b/modules/database/classes/Kohana/Database/Query/Builder/Select.php new file mode 100644 index 0000000..3492a71 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder/Select.php @@ -0,0 +1,446 @@ +_select = $columns; + } + + // Start the query with no actual SQL statement + parent::__construct(Database::SELECT, ''); + } + + /** + * Enables or disables selecting only unique columns using "SELECT DISTINCT" + * + * @param boolean $value enable or disable distinct columns + * @return $this + */ + public function distinct($value) + { + $this->_distinct = (bool) $value; + + return $this; + } + + /** + * Choose the columns to select from. + * + * @param mixed $columns column name or array($column, $alias) or object + * @return $this + */ + public function select($columns = NULL) + { + $columns = func_get_args(); + + $this->_select = array_merge($this->_select, $columns); + + return $this; + } + + /** + * Choose the columns to select from, using an array. + * + * @param array $columns list of column names or aliases + * @return $this + */ + public function select_array(array $columns) + { + $this->_select = array_merge($this->_select, $columns); + + return $this; + } + + /** + * Choose the tables to select "FROM ..." + * + * @param mixed $table table name or array($table, $alias) or object + * @return $this + */ + public function from($tables) + { + $tables = func_get_args(); + + $this->_from = array_merge($this->_from, $tables); + + return $this; + } + + /** + * Adds addition tables to "JOIN ...". + * + * @param mixed $table column name or array($column, $alias) or object + * @param string $type join type (LEFT, RIGHT, INNER, etc) + * @return $this + */ + public function join($table, $type = NULL) + { + $this->_join[] = $this->_last_join = new Database_Query_Builder_Join($table, $type); + + return $this; + } + + /** + * Adds "ON ..." conditions for the last created JOIN statement. + * + * @param mixed $c1 column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $c2 column name or array($column, $alias) or object + * @return $this + */ + public function on($c1, $op, $c2) + { + $this->_last_join->on($c1, $op, $c2); + + return $this; + } + + /** + * Adds "USING ..." conditions for the last created JOIN statement. + * + * @param string $columns column name + * @return $this + */ + public function using($columns) + { + $columns = func_get_args(); + + call_user_func_array(array($this->_last_join, 'using'), $columns); + + return $this; + } + + /** + * Creates a "GROUP BY ..." filter. + * + * @param mixed $columns column name or array($column, $alias) or object + * @return $this + */ + public function group_by($columns) + { + $columns = func_get_args(); + + $this->_group_by = array_merge($this->_group_by, $columns); + + return $this; + } + + /** + * Alias of and_having() + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function having($column, $op, $value = NULL) + { + return $this->and_having($column, $op, $value); + } + + /** + * Creates a new "AND HAVING" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function and_having($column, $op, $value = NULL) + { + $this->_having[] = array('AND' => array($column, $op, $value)); + + return $this; + } + + /** + * Creates a new "OR HAVING" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function or_having($column, $op, $value = NULL) + { + $this->_having[] = array('OR' => array($column, $op, $value)); + + return $this; + } + + /** + * Alias of and_having_open() + * + * @return $this + */ + public function having_open() + { + return $this->and_having_open(); + } + + /** + * Opens a new "AND HAVING (...)" grouping. + * + * @return $this + */ + public function and_having_open() + { + $this->_having[] = array('AND' => '('); + + return $this; + } + + /** + * Opens a new "OR HAVING (...)" grouping. + * + * @return $this + */ + public function or_having_open() + { + $this->_having[] = array('OR' => '('); + + return $this; + } + + /** + * Closes an open "AND HAVING (...)" grouping. + * + * @return $this + */ + public function having_close() + { + return $this->and_having_close(); + } + + /** + * Closes an open "AND HAVING (...)" grouping. + * + * @return $this + */ + public function and_having_close() + { + $this->_having[] = array('AND' => ')'); + + return $this; + } + + /** + * Closes an open "OR HAVING (...)" grouping. + * + * @return $this + */ + public function or_having_close() + { + $this->_having[] = array('OR' => ')'); + + return $this; + } + + /** + * Adds an other UNION clause. + * + * @param mixed $select if string, it must be the name of a table. Else + * must be an instance of Database_Query_Builder_Select + * @param boolean $all decides if it's an UNION or UNION ALL clause + * @return $this + */ + public function union($select, $all = TRUE) + { + if (is_string($select)) + { + $select = DB::select()->from($select); + } + if ( ! $select instanceof Database_Query_Builder_Select) + throw new Kohana_Exception('first parameter must be a string or an instance of Database_Query_Builder_Select'); + $this->_union []= array('select' => $select, 'all' => $all); + return $this; + } + + /** + * Start returning results after "OFFSET ..." + * + * @param integer $number starting result number or NULL to reset + * @return $this + */ + public function offset($number) + { + $this->_offset = $number; + + return $this; + } + + /** + * Compile the SQL query and return it. + * + * @param mixed $db Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + // Callback to quote columns + $quote_column = array($db, 'quote_column'); + + // Callback to quote tables + $quote_table = array($db, 'quote_table'); + + // Start a selection query + $query = 'SELECT '; + + if ($this->_distinct === TRUE) + { + // Select only unique results + $query .= 'DISTINCT '; + } + + if (empty($this->_select)) + { + // Select all columns + $query .= '*'; + } + else + { + // Select all columns + $query .= implode(', ', array_unique(array_map($quote_column, $this->_select))); + } + + if ( ! empty($this->_from)) + { + // Set tables to select from + $query .= ' FROM '.implode(', ', array_unique(array_map($quote_table, $this->_from))); + } + + if ( ! empty($this->_join)) + { + // Add tables to join + $query .= ' '.$this->_compile_join($db, $this->_join); + } + + if ( ! empty($this->_where)) + { + // Add selection conditions + $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where); + } + + if ( ! empty($this->_group_by)) + { + // Add grouping + $query .= ' '.$this->_compile_group_by($db, $this->_group_by); + } + + if ( ! empty($this->_having)) + { + // Add filtering conditions + $query .= ' HAVING '.$this->_compile_conditions($db, $this->_having); + } + + if ( ! empty($this->_order_by)) + { + // Add sorting + $query .= ' '.$this->_compile_order_by($db, $this->_order_by); + } + + if ($this->_limit !== NULL) + { + // Add limiting + $query .= ' LIMIT '.$this->_limit; + } + + if ($this->_offset !== NULL) + { + // Add offsets + $query .= ' OFFSET '.$this->_offset; + } + + if ( ! empty($this->_union)) + { + foreach ($this->_union as $u) { + $query .= ' UNION '; + if ($u['all'] === TRUE) + { + $query .= 'ALL '; + } + $query .= $u['select']->compile($db); + } + } + + $this->_sql = $query; + + return parent::compile($db); + } + + public function reset() + { + $this->_select = + $this->_from = + $this->_join = + $this->_where = + $this->_group_by = + $this->_having = + $this->_order_by = + $this->_union = array(); + + $this->_distinct = FALSE; + + $this->_limit = + $this->_offset = + $this->_last_join = NULL; + + $this->_parameters = array(); + + $this->_sql = NULL; + + return $this; + } + +} // End Database_Query_Select diff --git a/modules/database/classes/Kohana/Database/Query/Builder/Update.php b/modules/database/classes/Kohana/Database/Query/Builder/Update.php new file mode 100644 index 0000000..e6e3da5 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder/Update.php @@ -0,0 +1,140 @@ +_table = $table; + } + + // Start the query with no SQL + return parent::__construct(Database::UPDATE, ''); + } + + /** + * Sets the table to update. + * + * @param mixed $table table name or array($table, $alias) or object + * @return $this + */ + public function table($table) + { + $this->_table = $table; + + return $this; + } + + /** + * Set the values to update with an associative array. + * + * @param array $pairs associative (column => value) list + * @return $this + */ + public function set(array $pairs) + { + foreach ($pairs as $column => $value) + { + $this->_set[] = array($column, $value); + } + + return $this; + } + + /** + * Set the value of a single column. + * + * @param mixed $column table name or array($table, $alias) or object + * @param mixed $value column value + * @return $this + */ + public function value($column, $value) + { + $this->_set[] = array($column, $value); + + return $this; + } + + /** + * Compile the SQL query and return it. + * + * @param mixed $db Database instance or name of instance + * @return string + */ + public function compile($db = NULL) + { + if ( ! is_object($db)) + { + // Get the database instance + $db = Database::instance($db); + } + + // Start an update query + $query = 'UPDATE '.$db->quote_table($this->_table); + + // Add the columns to update + $query .= ' SET '.$this->_compile_set($db, $this->_set); + + if ( ! empty($this->_where)) + { + // Add selection conditions + $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where); + } + + if ( ! empty($this->_order_by)) + { + // Add sorting + $query .= ' '.$this->_compile_order_by($db, $this->_order_by); + } + + if ($this->_limit !== NULL) + { + // Add limiting + $query .= ' LIMIT '.$this->_limit; + } + + $this->_sql = $query; + + return parent::compile($db); + } + + public function reset() + { + $this->_table = NULL; + + $this->_set = + $this->_where = array(); + + $this->_limit = NULL; + + $this->_parameters = array(); + + $this->_sql = NULL; + + return $this; + } + + +} // End Database_Query_Builder_Update diff --git a/modules/database/classes/Kohana/Database/Query/Builder/Where.php b/modules/database/classes/Kohana/Database/Query/Builder/Where.php new file mode 100644 index 0000000..58f6b5d --- /dev/null +++ b/modules/database/classes/Kohana/Database/Query/Builder/Where.php @@ -0,0 +1,180 @@ +and_where($column, $op, $value); + } + + /** + * Creates a new "AND WHERE" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function and_where($column, $op, $value) + { + $this->_where[] = array('AND' => array($column, $op, $value)); + + return $this; + } + + /** + * Creates a new "OR WHERE" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function or_where($column, $op, $value) + { + $this->_where[] = array('OR' => array($column, $op, $value)); + + return $this; + } + + /** + * Alias of and_where_open() + * + * @return $this + */ + public function where_open() + { + return $this->and_where_open(); + } + + /** + * Opens a new "AND WHERE (...)" grouping. + * + * @return $this + */ + public function and_where_open() + { + $this->_where[] = array('AND' => '('); + + return $this; + } + + /** + * Opens a new "OR WHERE (...)" grouping. + * + * @return $this + */ + public function or_where_open() + { + $this->_where[] = array('OR' => '('); + + return $this; + } + + /** + * Closes an open "WHERE (...)" grouping. + * + * @return $this + */ + public function where_close() + { + return $this->and_where_close(); + } + + /** + * Closes an open "WHERE (...)" grouping or removes the grouping when it is + * empty. + * + * @return $this + */ + public function where_close_empty() + { + $group = end($this->_where); + + if ($group AND reset($group) === '(') + { + array_pop($this->_where); + + return $this; + } + + return $this->where_close(); + } + + /** + * Closes an open "WHERE (...)" grouping. + * + * @return $this + */ + public function and_where_close() + { + $this->_where[] = array('AND' => ')'); + + return $this; + } + + /** + * Closes an open "WHERE (...)" grouping. + * + * @return $this + */ + public function or_where_close() + { + $this->_where[] = array('OR' => ')'); + + return $this; + } + + /** + * Applies sorting with "ORDER BY ..." + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $direction direction of sorting + * @return $this + */ + public function order_by($column, $direction = NULL) + { + $this->_order_by[] = array($column, $direction); + + return $this; + } + + /** + * Return up to "LIMIT ..." results + * + * @param integer $number maximum results to return or NULL to reset + * @return $this + */ + public function limit($number) + { + $this->_limit = $number; + + return $this; + } + +} // End Database_Query_Builder_Where diff --git a/modules/database/classes/Kohana/Database/Result.php b/modules/database/classes/Kohana/Database/Result.php new file mode 100644 index 0000000..3c3284f --- /dev/null +++ b/modules/database/classes/Kohana/Database/Result.php @@ -0,0 +1,338 @@ +_result = $result; + + // Store the SQL locally + $this->_query = $sql; + + if (is_object($as_object)) + { + // Get the object class name + $as_object = get_class($as_object); + } + + // Results as objects or associative arrays + $this->_as_object = $as_object; + + if ($params) + { + // Object constructor params + $this->_object_params = $params; + } + } + + /** + * Result destruction cleans up all open result sets. + * + * @return void + */ + abstract public function __destruct(); + + /** + * Get a cached database result from the current result iterator. + * + * $cachable = serialize($result->cached()); + * + * @return Database_Result_Cached + * @since 3.0.5 + */ + public function cached() + { + return new Database_Result_Cached($this->as_array(), $this->_query, $this->_as_object); + } + + /** + * Return all of the rows in the result as an array. + * + * // Indexed array of all rows + * $rows = $result->as_array(); + * + * // Associative array of rows by "id" + * $rows = $result->as_array('id'); + * + * // Associative array of rows, "id" => "name" + * $rows = $result->as_array('id', 'name'); + * + * @param string $key column for associative keys + * @param string $value column for values + * @return array + */ + public function as_array($key = NULL, $value = NULL) + { + $results = array(); + + if ($key === NULL AND $value === NULL) + { + // Indexed rows + + foreach ($this as $row) + { + $results[] = $row; + } + } + elseif ($key === NULL) + { + // Indexed columns + + if ($this->_as_object) + { + foreach ($this as $row) + { + $results[] = $row->$value; + } + } + else + { + foreach ($this as $row) + { + $results[] = $row[$value]; + } + } + } + elseif ($value === NULL) + { + // Associative rows + + if ($this->_as_object) + { + foreach ($this as $row) + { + $results[$row->$key] = $row; + } + } + else + { + foreach ($this as $row) + { + $results[$row[$key]] = $row; + } + } + } + else + { + // Associative columns + + if ($this->_as_object) + { + foreach ($this as $row) + { + $results[$row->$key] = $row->$value; + } + } + else + { + foreach ($this as $row) + { + $results[$row[$key]] = $row[$value]; + } + } + } + + $this->rewind(); + + return $results; + } + + /** + * Return the named column from the current row. + * + * // Get the "id" value + * $id = $result->get('id'); + * + * @param string $name column to get + * @param mixed $default default value if the column does not exist + * @return mixed + */ + public function get($name, $default = NULL) + { + $row = $this->current(); + + if ($this->_as_object) + { + if (isset($row->$name)) + return $row->$name; + } + else + { + if (isset($row[$name])) + return $row[$name]; + } + + return $default; + } + + /** + * Implements [Countable::count], returns the total number of rows. + * + * echo count($result); + * + * @return integer + */ + public function count() + { + return $this->_total_rows; + } + + /** + * Implements [ArrayAccess::offsetExists], determines if row exists. + * + * if (isset($result[10])) + * { + * // Row 10 exists + * } + * + * @param int $offset + * @return boolean + */ + public function offsetExists($offset) + { + return ($offset >= 0 AND $offset < $this->_total_rows); + } + + /** + * Implements [ArrayAccess::offsetGet], gets a given row. + * + * $row = $result[10]; + * + * @param int $offset + * @return mixed + */ + public function offsetGet($offset) + { + if ( ! $this->seek($offset)) + return NULL; + + return $this->current(); + } + + /** + * Implements [ArrayAccess::offsetSet], throws an error. + * + * [!!] You cannot modify a database result. + * + * @param int $offset + * @param mixed $value + * @return void + * @throws Kohana_Exception + */ + final public function offsetSet($offset, $value) + { + throw new Kohana_Exception('Database results are read-only'); + } + + /** + * Implements [ArrayAccess::offsetUnset], throws an error. + * + * [!!] You cannot modify a database result. + * + * @param int $offset + * @return void + * @throws Kohana_Exception + */ + final public function offsetUnset($offset) + { + throw new Kohana_Exception('Database results are read-only'); + } + + /** + * Implements [Iterator::key], returns the current row number. + * + * echo key($result); + * + * @return integer + */ + public function key() + { + return $this->_current_row; + } + + /** + * Implements [Iterator::next], moves to the next row. + * + * next($result); + * + * @return $this + */ + public function next() + { + ++$this->_current_row; + return $this; + } + + /** + * Implements [Iterator::prev], moves to the previous row. + * + * prev($result); + * + * @return $this + */ + public function prev() + { + --$this->_current_row; + return $this; + } + + /** + * Implements [Iterator::rewind], sets the current row to zero. + * + * rewind($result); + * + * @return $this + */ + public function rewind() + { + $this->_current_row = 0; + return $this; + } + + /** + * Implements [Iterator::valid], checks if the current row exists. + * + * [!!] This method is only used internally. + * + * @return boolean + */ + public function valid() + { + return $this->offsetExists($this->_current_row); + } + +} // End Database_Result diff --git a/modules/database/classes/Kohana/Database/Result/Cached.php b/modules/database/classes/Kohana/Database/Result/Cached.php new file mode 100644 index 0000000..8af0854 --- /dev/null +++ b/modules/database/classes/Kohana/Database/Result/Cached.php @@ -0,0 +1,51 @@ +_total_rows = count($result); + } + + public function __destruct() + { + // Cached results do not use resources + } + + public function cached() + { + return $this; + } + + public function seek($offset) + { + if ($this->offsetExists($offset)) + { + $this->_current_row = $offset; + + return TRUE; + } + else + { + return FALSE; + } + } + + public function current() + { + // Return an array of the row + return $this->valid() ? $this->_result[$this->_current_row] : NULL; + } + +} // End Database_Result_Cached diff --git a/modules/database/classes/Kohana/Model/Database.php b/modules/database/classes/Kohana/Model/Database.php new file mode 100644 index 0000000..790e8f0 --- /dev/null +++ b/modules/database/classes/Kohana/Model/Database.php @@ -0,0 +1,63 @@ +_db = $db; + } + elseif ( ! $this->_db) + { + // Use the default name + $this->_db = Database::$default; + } + + if (is_string($this->_db)) + { + // Load the database + $this->_db = Database::instance($this->_db); + } + } + +} // End Model diff --git a/modules/database/classes/Kohana/Session/Database.php b/modules/database/classes/Kohana/Session/Database.php new file mode 100644 index 0000000..34abbbd --- /dev/null +++ b/modules/database/classes/Kohana/Session/Database.php @@ -0,0 +1,239 @@ + 'session_id', + 'last_active' => 'last_active', + 'contents' => 'contents' + ); + + // Garbage collection requests + protected $_gc = 500; + + // The current session id + protected $_session_id; + + // The old session id + protected $_update_id; + + public function __construct(array $config = NULL, $id = NULL) + { + if ( ! isset($config['group'])) + { + // Use the default group + $config['group'] = Database::$default; + } + + // Load the database + $this->_db = Database::instance($config['group']); + + if (isset($config['table'])) + { + // Set the table name + $this->_table = (string) $config['table']; + } + + if (isset($config['gc'])) + { + // Set the gc chance + $this->_gc = (int) $config['gc']; + } + + if (isset($config['columns'])) + { + // Overload column names + $this->_columns = $config['columns']; + } + + parent::__construct($config, $id); + + if (mt_rand(0, $this->_gc) === $this->_gc) + { + // Run garbage collection + // This will average out to run once every X requests + $this->_gc(); + } + } + + public function id() + { + return $this->_session_id; + } + + protected function _read($id = NULL) + { + if ($id OR $id = Cookie::get($this->_name)) + { + $result = DB::select(array($this->_columns['contents'], 'contents')) + ->from($this->_table) + ->where($this->_columns['session_id'], '=', ':id') + ->limit(1) + ->param(':id', $id) + ->execute($this->_db); + + if ($result->count()) + { + // Set the current session id + $this->_session_id = $this->_update_id = $id; + + // Return the contents + return $result->get('contents'); + } + } + + // Create a new session id + $this->_regenerate(); + + return NULL; + } + + protected function _regenerate() + { + // Create the query to find an ID + $query = DB::select($this->_columns['session_id']) + ->from($this->_table) + ->where($this->_columns['session_id'], '=', ':id') + ->limit(1) + ->bind(':id', $id); + + do + { + // Create a new session id + $id = str_replace('.', '-', uniqid(NULL, TRUE)); + + // Get the the id from the database + $result = $query->execute($this->_db); + } + while ($result->count()); + + return $this->_session_id = $id; + } + + protected function _write() + { + if ($this->_update_id === NULL) + { + // Insert a new row + $query = DB::insert($this->_table, $this->_columns) + ->values(array(':new_id', ':active', ':contents')); + } + else + { + // Update the row + $query = DB::update($this->_table) + ->value($this->_columns['last_active'], ':active') + ->value($this->_columns['contents'], ':contents') + ->where($this->_columns['session_id'], '=', ':old_id'); + + if ($this->_update_id !== $this->_session_id) + { + // Also update the session id + $query->value($this->_columns['session_id'], ':new_id'); + } + } + + $query + ->param(':new_id', $this->_session_id) + ->param(':old_id', $this->_update_id) + ->param(':active', $this->_data['last_active']) + ->param(':contents', $this->__toString()); + + // Execute the query + $query->execute($this->_db); + + // The update and the session id are now the same + $this->_update_id = $this->_session_id; + + // Update the cookie with the new session id + Cookie::set($this->_name, $this->_session_id, $this->_lifetime); + + return TRUE; + } + + /** + * @return bool + */ + protected function _restart() + { + $this->_regenerate(); + + return TRUE; + } + + protected function _destroy() + { + if ($this->_update_id === NULL) + { + // Session has not been created yet + return TRUE; + } + + // Delete the current session + $query = DB::delete($this->_table) + ->where($this->_columns['session_id'], '=', ':id') + ->param(':id', $this->_update_id); + + try + { + // Execute the query + $query->execute($this->_db); + + // Delete the cookie + Cookie::delete($this->_name); + } + catch (Exception $e) + { + // An error occurred, the session has not been deleted + return FALSE; + } + + return TRUE; + } + + protected function _gc() + { + if ($this->_lifetime) + { + // Expire sessions when their lifetime is up + $expires = $this->_lifetime; + } + else + { + // Expire sessions after one month + $expires = Date::MONTH; + } + + // Delete all sessions that have expired + DB::delete($this->_table) + ->where($this->_columns['last_active'], '<', ':time') + ->param(':time', time() - $expires) + ->execute($this->_db); + } + +} // End Session_Database diff --git a/modules/database/classes/Model/Database.php b/modules/database/classes/Model/Database.php new file mode 100644 index 0000000..3b6e609 --- /dev/null +++ b/modules/database/classes/Model/Database.php @@ -0,0 +1,3 @@ + array + ( + 'type' => 'MySQL', + 'connection' => array( + /** + * The following options are available for MySQL: + * + * string hostname server hostname, or socket + * string database database name + * string username database username + * string password database password + * boolean persistent use persistent connections? + * array variables system variables as "key => value" pairs + * + * Ports and sockets may be appended to the hostname. + */ + 'hostname' => 'localhost', + 'database' => 'kohana', + 'username' => FALSE, + 'password' => FALSE, + 'persistent' => FALSE, + ), + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => FALSE, + ), + 'alternate' => array( + 'type' => 'PDO', + 'connection' => array( + /** + * The following options are available for PDO: + * + * string dsn Data Source Name + * string username database username + * string password database password + * boolean persistent use persistent connections? + */ + 'dsn' => 'mysql:host=localhost;dbname=kohana', + 'username' => 'root', + 'password' => 'r00tdb', + 'persistent' => FALSE, + ), + /** + * The following extra options are available for PDO: + * + * string identifier set the escaping identifier + */ + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => FALSE, + ), +); diff --git a/modules/database/config/session.php b/modules/database/config/session.php new file mode 100644 index 0000000..58263ae --- /dev/null +++ b/modules/database/config/session.php @@ -0,0 +1,27 @@ + array( + /** + * Database settings for session storage. + * + * string group configuation group name + * string table session table name + * integer gc number of requests before gc is invoked + * columns array custom column names + */ + 'group' => 'default', + 'table' => 'sessions', + 'gc' => 500, + 'columns' => array( + /** + * session_id: session identifier + * last_active: timestamp of the last activity + * contents: serialized session data + */ + 'session_id' => 'session_id', + 'last_active' => 'last_active', + 'contents' => 'contents' + ), + ), +); diff --git a/modules/database/config/userguide.php b/modules/database/config/userguide.php new file mode 100644 index 0000000..88ff2a3 --- /dev/null +++ b/modules/database/config/userguide.php @@ -0,0 +1,23 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'database' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'Database', + + // A short description of this module, shown on the index page + 'description' => 'Database agnostic querying and result management.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2012 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/modules/database/guide/database/config.md b/modules/database/guide/database/config.md new file mode 100644 index 0000000..240a6bb --- /dev/null +++ b/modules/database/guide/database/config.md @@ -0,0 +1,116 @@ +# Configuration + +The default config file is located in `MODPATH/database/config/database.php`. You should copy this file to `APPPATH/config/database.php` and make changes there, in keeping with the [cascading filesystem](../kohana/files). + +The database configuration file contains an array of configuration groups. The structure of each database configuration group, called an "instance", looks like this: + + string INSTANCE_NAME => array( + 'type' => string DATABASE_TYPE, + 'connection' => array CONNECTION_ARRAY, + 'table_prefix' => string TABLE_PREFIX, + 'charset' => string CHARACTER_SET, + ), + +Understanding each of these settings is important. + +INSTANCE_NAME +: Connections can be named anything you want, but you should always have at least one connection called "default". + +DATABASE_TYPE +: One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers. Drivers must extend the Database class. + +CONNECTION_ARRAY +: Specific driver options for connecting to your database. (Driver options are explained [below](#connection-settings).) + +TABLE_PREFIX +: Prefix that will be added to all table names by the [query builder](#query_building). + + +## Example + +The example file below shows 2 MySQL connections, one local and one remote. + + return array + ( + 'default' => array + ( + 'type' => 'mysql', + 'connection' => array( + 'hostname' => 'localhost', + 'username' => 'dbuser', + 'password' => 'mypassword', + 'persistent' => FALSE, + 'database' => 'my_db_name', + ), + 'table_prefix' => '', + 'charset' => 'utf8', + ), + 'remote' => array( + 'type' => 'mysql', + 'connection' => array( + 'hostname' => '55.55.55.55', + 'username' => 'remote_user', + 'password' => 'mypassword', + 'persistent' => FALSE, + 'database' => 'my_remote_db_name', + ), + 'table_prefix' => '', + 'charset' => 'utf8', + ), + ); + +## Connections and Instances + +Each configuration group is referred to as a database instance. Each instance can be accessed by calling [Database::instance]. If you don't provide a parameter, the default instance is used. + + // This would connect to the database defined as 'default' + $default = Database::instance(); + + // This would connect to the database defined as 'remote' + $remote = Database::instance('remote'); + +To disconnect the database, simply destroy the object: + + unset($default) + + // Or + + unset(Database::$instances['default']); + +If you want to disconnect all of the database instances at once: + + Database::$instances = array(); + +## Connection Settings + +Every database driver has different connection settings. + +### MySQL + +A [MySQL database](http://www.php.net/manual/en/book.mysql.php) can accept the following options in the `connection` array: + +Type | Option | Description | Default value +----------|------------|----------------------------| ------------------------- +`string` | hostname | Hostname of the database | `localhost` +`integer` | port | Port number | `NULL` +`string` | socket | UNIX socket | `NULL` +`string` | username | Database username | `NULL` +`string` | password | Database password | `NULL` +`boolean` | persistent | Persistent connections | `FALSE` +`string` | database | Database name | `kohana` + +### PDO + +A [PDO database](http://php.net/manual/en/book.pdo.php) can accept these options in the `connection` array: + +Type | Option | Description | Default value +----------|------------|----------------------------| ------------------------- +`string` | dsn | PDO data source identifier | `localhost` +`array` | options | Driver-specific options | none +`string` | username | Database username | `NULL` +`string` | password | Database password | `NULL` +`boolean` | persistent | Persistent connections | `FALSE` + +The connection character set should be configured using the DSN string or `options` array. + +[!!] If you are using PDO and are not sure what to use for the `dsn` option, review [PDO::__construct](http://php.net/pdo.construct). \ No newline at end of file diff --git a/modules/database/guide/database/examples.md b/modules/database/guide/database/examples.md new file mode 100644 index 0000000..6a9d1b5 --- /dev/null +++ b/modules/database/guide/database/examples.md @@ -0,0 +1,52 @@ +# Examples + +Here are some "real world" examples of using the database library to construct your queries and use the results. + +## Examples of Parameterized Statements + +TODO: 4-6 examples of parameterized statements of varying complexity, including a good bind() example. + +## Pagination and search/filter + +In this example, we loop through an array of whitelisted input fields and for each allowed non-empty value we add it to the search query. We make a clone of the query and then execute that query to count the total number of results. The count is then passed to the [Pagination](../pagination) class to determine the search offset. The last few lines search with Pagination's items_per_page and offset values to return a page of results based on the current page the user is on. + + $query = DB::select()->from('users'); + + //only search for these fields + $form_inputs = array('first_name', 'last_name', 'email'); + foreach ($form_inputs as $name) + { + $value = Arr::get($_GET, $name, FALSE); + if ($value !== FALSE AND $value != '') + { + $query->where($name, 'like', '%'.$value.'%'); + } + } + + //copy the query & execute it + $pagination_query = clone $query; + $count = $pagination_query->select(DB::expr('COUNT(*)) AS mycount')->execute()->get('mycount'); + + //pass the total item count to Pagination + $config = Kohana::$config->load('pagination'); + $pagination = Pagination::factory(array( + 'total_items' => $count, + 'current_page' => array('source' => 'route', 'key' => 'page'), + 'items_per_page' => 20, + 'view' => 'pagination/pretty', + 'auto_hide' => TRUE, + )); + $page_links = $pagination->render(); + + //search for results starting at the offset calculated by the Pagination class + $query->order_by('last_name', 'asc') + ->order_by('first_name', 'asc') + ->limit($pagination->items_per_page) + ->offset($pagination->offset); + $results = $query->execute()->as_array(); + +## Having + +TODO: example goes here + +[!!] We could use more examples on this page. \ No newline at end of file diff --git a/modules/database/guide/database/index.md b/modules/database/guide/database/index.md new file mode 100644 index 0000000..162651d --- /dev/null +++ b/modules/database/guide/database/index.md @@ -0,0 +1,17 @@ +# Database + +Kohana 3.0 comes with a robust module for working with databases. By default, the database module supports drivers for [MySQL](http://php.net/mysql) and [PDO](http://php.net/pdo), but new drivers can be made for other database servers. + +The database module is included with the Kohana 3.0 install, but needs to be enabled before you can use it. To enable, open your `application/bootstrap.php` file and modify the call to [Kohana::modules] by including the database module like so: + + Kohana::modules(array( + ... + 'database' => MODPATH.'database', + ... + )); + +Next, you will then need to [configure](config) the database module to connect to your database. + +Once that is done then you can make [queries](query) and use the [results](results). + +The database module also provides a [config driver](../api/Kohana_Config_Database) (for storing [configuration](../kohana/files/config) in the database) and a [session driver](Session_Database). diff --git a/modules/database/guide/database/menu.md b/modules/database/guide/database/menu.md new file mode 100644 index 0000000..e1c06a8 --- /dev/null +++ b/modules/database/guide/database/menu.md @@ -0,0 +1,7 @@ +## [Database]() +- [Configuration](config) +- [Querying](query) + - [Parameterized Statements](query/parameterized) + - [Query Builder](query/builder) +- [Results](results) +- [Examples](examples) \ No newline at end of file diff --git a/modules/database/guide/database/query.md b/modules/database/guide/database/query.md new file mode 100644 index 0000000..0a15bf5 --- /dev/null +++ b/modules/database/guide/database/query.md @@ -0,0 +1,5 @@ +# Making Queries + +There are two different ways to make queries. The simplest way to make a query is to use [Database_Query], via [DB::query], to manually create queries. These queries are called [parameterized statements](query/parameterized) and allow you to set query parameters which are automatically escaped. The second way to make a query is by building the query using method calls. This is done using the [query builder](query/builder). + +[!!] All queries are run using the `execute` method, which accepts a [Database] object or instance name. See [Database_Query::execute] for more information. \ No newline at end of file diff --git a/modules/database/guide/database/query/builder.md b/modules/database/guide/database/query/builder.md new file mode 100644 index 0000000..d2fd893 --- /dev/null +++ b/modules/database/guide/database/query/builder.md @@ -0,0 +1,251 @@ +# Query Builder + +Creating queries dynamically using objects and methods allows queries to be written very quickly in an agnostic way. Query building also adds identifier (table and column name) quoting, as well as value quoting. + +## Select + +Each type of database query is represented by a different class, each with their own methods. For instance, to create a SELECT query, we use [DB::select] which is a shortcut to return a new [Database_Query_Builder_Select] object: + + $query = DB::select(); + +Query Builder methods return a reference to itself so that method chaining may be used. Select queries ussually require a table and they are referenced using the `from()` method. The `from()` method takes one parameter which can be the table name (string), an array of two strings (table name and alias), or an object (See Subqueries in the Advanced Queries section below). + + $query = DB::select()->from('users'); + +Limiting the results of queries is done using the `where()`, `and_where()` and `or_where()` methods. These methods take three parameters: a column, an operator, and a value. + + $query = DB::select()->from('users')->where('username', '=', 'john'); + +Multiple `where()` methods may be used to string together multiple clauses connected by the boolean operator in the method's prefix. The `where()` method is a wrapper that just calls `and_where()`. + + $query = DB::select()->from('users')->where('username', '=', 'john')->or_where('username', '=', 'jane'); + +You can use any operator you want. Examples include `IN`, `BETWEEN`, `>`, `=<`, `!=`, etc. Use an array for operators that require more than one value. + + $query = DB::select()->from('users')->where('logins', '<=', 1); + + $query = DB::select()->from('users')->where('logins', '>', 50); + + $query = DB::select()->from('users')->where('username', 'IN', array('john','mark','matt')); + + $query = DB::select()->from('users')->where('joindate', 'BETWEEN', array($then, $now)); + +By default, [DB::select] will select all columns (`SELECT * ...`), but you can also specify which columns you want returned by passing parameters to [DB::select]: + + $query = DB::select('username', 'password')->from('users')->where('username', '=', 'john'); + +Now take a minute to look at what this method chain is doing. First, we create a new selection object using the [DB::select] method. Next, we set table(s) using the `from()` method. Last, we search for a specific records using the `where()` method. We can display the SQL that will be executed by casting the query to a string: + + echo Debug::vars((string) $query); + // Should display: + // SELECT `username`, `password` FROM `users` WHERE `username` = 'john' + +Notice how the column and table names are automatically escaped, as well as the values? This is one of the key benefits of using the query builder. + +### Select - AS (column aliases) + +It is also possible to create `AS` aliases when selecting, by passing an array as each parameter to [DB::select]: + + $query = DB::select(array('username', 'u'), array('password', 'p'))->from('users'); + +This query would generate the following SQL: + + SELECT `username` AS `u`, `password` AS `p` FROM `users` + +### Select - DISTINCT + +Unique column values may be turned on or off (default) by passing TRUE or FALSE, respectively, to the `distinct()` method. + + $query = DB::select('username')->distinct(TRUE)->from('posts'); + +This query would generate the following SQL: + + SELECT DISTINCT `username` FROM `posts` + +### Select - LIMIT & OFFSET + +When querying large sets of data, it is often better to limit the results and page through the data one chunk at a time. This is done using the `limit()` and `offset()` methods. + + $query = DB::select()->from(`posts`)->limit(10)->offset(30); + +This query would generate the following SQL: + + SELECT * FROM `posts` LIMIT 10 OFFSET 30 + +### Select - ORDER BY + +Often you will want the results in a particular order and rather than sorting the results, it's better to have the results returned to you in the correct order. You can do this by using the order_by() method. It takes the column name and an optional direction string as the parameters. Multiple `order_by()` methods can be used to add additional sorting capability. + + $query = DB::select()->from(`posts`)->order_by(`published`, `DESC`); + +This query would generate the following SQL: + + SELECT * FROM `posts` ORDER BY `published` DESC + +[!!] For a complete list of methods available while building a select query see [Database_Query_Builder_Select]. + +## Insert + +To create records into the database, use [DB::insert] to create an INSERT query, using `values()` to pass in the data: + + $query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd')); + +This query would generate the following SQL: + + INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd') + +[!!] For a complete list of methods available while building an insert query see [Database_Query_Builder_Insert]. + +## Update + +To modify an existing record, use [DB::update] to create an UPDATE query: + + $query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john'); + +This query would generate the following SQL: + + UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john' + +[!!] For a complete list of methods available while building an update query see [Database_Query_Builder_Update]. + +## Delete + +To remove an existing record, use [DB::delete] to create a DELETE query: + + $query = DB::delete('users')->where('username', 'IN', array('john', 'jane')); + +This query would generate the following SQL: + + DELETE FROM `users` WHERE `username` IN ('john', 'jane') + +[!!] For a complete list of methods available while building a delete query see [Database_Query_Builder_Delete]. + +## Advanced Queries + +### Joins + +Multiple tables can be joined using the `join()` and `on()` methods. The `join()` method takes two parameters. The first is either a table name, an array containing the table and alias, or an object (subquery or expression). The second parameter is the join type: LEFT, RIGHT, INNER, etc. + +The `on()` method sets the conditions for the previous `join()` method and is very similar to the `where()` method in that it takes three parameters; left column (name or object), an operator, and the right column (name or object). Multiple `on()` methods may be used to supply multiple conditions and they will be appended with an 'AND' operator. + + // This query will find all the posts related to "smith" with JOIN + $query = DB::select('authors.name', 'posts.content')->from('authors')->join('posts')->on('authors.id', '=', 'posts.author_id')->where('authors.name', '=', 'smith'); + +This query would generate the following SQL: + + SELECT `authors`.`name`, `posts`.`content` FROM `authors` JOIN `posts` ON (`authors`.`id` = `posts`.`author_id`) WHERE `authors`.`name` = 'smith' + +If you want to do a LEFT, RIGHT or INNER JOIN you would do it like this `join('colum_name', 'type_of_join')`: + + // This query will find all the posts related to "smith" with LEFT JOIN + $query = DB::select()->from('authors')->join('posts', 'LEFT')->on('authors.id', '=', 'posts.author_id')->where('authors.name', '=', 'smith'); + +This query would generate the following SQL: + + SELECT `authors`.`name`, `posts`.`content` FROM `authors` LEFT JOIN `posts` ON (`authors`.`id` = `posts`.`author_id`) WHERE `authors`.`name` = 'smith' + +[!!] When joining multiple tables with similar column names, it's best to prefix the columns with the table name or table alias to avoid errors. Ambiguous column names should also be aliased so that they can be referenced easier. + +### Database Functions + +Eventually you will probably run into a situation where you need to call `COUNT` or some other database function within your query. The query builder supports these functions using the `Database_Expression` class: + + $query = DB::select(array(DB::expr('COUNT(`username`)'), 'total_users'))->from('users'); + +This looks almost exactly the same as a standard `AS` alias, but note how the column name is put in a call to `DB::expr()`. Any time `DB::expr()` is used, the column name will **not** be escaped. This query would generate the following SQL: + + SELECT COUNT(`username`) AS `total_users` FROM `users` + +[!!] When building complex queries and you need to get a count of the total rows that will be returned, build the expression with an empty column list first. Then clone the query and add the COUNT function to one copy and the columns list to the other. This will cut down on the total lines of code and make updating the query easier. + + $query = DB::select()->from('users') + ->join('posts')->on('posts.username', '=', 'users.username') + ->where('users.active', '=', TRUE) + ->where('posts.created', '>=', $yesterday); + + $total = clone $query; + $total->select(array(DB::expr('COUNT( DISTINCT `username`)'), 'unique_users')); + $query->select('posts.username')->distinct(); + +### Aggregate Functions + +Aggregate functions like `COUNT()`, `SUM()`, `AVG()`, etc. will most likely be used with the `group_by()` and possibly the `having()` methods in order to group and filter the results on a set of columns. + + $query = DB::select('username', array(DB::expr('COUNT(`id`)'), 'total_posts') + ->from('posts')->group_by('username')->having('total_posts', '>=', 10); + +This will generate the following query: + + SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 + +### Subqueries + +Query Builder objects can be passed as parameters to many of the methods to create subqueries. Let's take the previous example query and pass it to a new query. + + $sub = DB::select('username', array(DB::expr('COUNT(`id`)'), 'total_posts') + ->from('posts')->group_by('username')->having('total_posts', '>=', 10); + + $query = DB::select('profiles.*', 'posts.total_posts')->from('profiles') + ->join(array($sub, 'posts'), 'INNER')->on('profiles.username', '=', 'posts.username'); + +This will generate the following query: + + SELECT `profiles`.*, `posts`.`total_posts` FROM `profiles` INNER JOIN + ( SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 ) AS posts + ON `profiles`.`username` = `posts`.`username` + +Insert queries can also use a select query for the input values + + $sub = DB::select('username', array(DB::expr('COUNT(`id`)'), 'total_posts') + ->from('posts')->group_by('username')->having('total_posts', '>=', 10); + + $query = DB::insert('post_totals', array('username', 'posts'))->select($sub); + +This will generate the following query: + + INSERT INTO `post_totals` (`username`, `posts`) + SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 + +### Boolean Operators and Nested Clauses + +Multiple Where and Having clauses are added to the query with Boolean operators connecting each expression. The default operator for both methods is AND which is the same as the and_ prefixed method. The OR operator can be specified by prefixing the methods with or_. Where and Having clauses can be nested or grouped by post fixing either method with _open and then followed by a method with a _close. + + $query = DB::select()->from('users') + ->where_open() + ->or_where('id', 'IN', $expired) + ->and_where_open() + ->where('last_login', '<=', $last_month) + ->or_where('last_login', 'IS', NULL) + ->and_where_close() + ->where_close() + ->and_where('removed','IS', NULL); + +This will generate the following query: + + SELECT * FROM `users` WHERE ( `id` IN (1, 2, 3, 5) OR ( `last_login` <= 1276020805 OR `last_login` IS NULL ) ) AND `removed` IS NULL + +### Database Expressions + +There are cases were you need a complex expression or other database functions, which you don't want the Query Builder to try and escape. In these cases, you will need to use a database expression created with [DB::expr]. **A database expression is taken as direct input and no escaping is performed.** + + $query = DB::update('users')->set(array('login_count' => DB::expr('login_count + 1')))->where('id', '=', $id); + +This will generate the following query, assuming `$id = 45`: + + UPDATE `users` SET `login_count` = `login_count` + 1 WHERE `id` = 45 + +Another example to calculate the distance of two geographical points: + + $query = DB::select(array(DB::expr('degrees(acos(sin(radians('.$lat.')) * sin(radians(`latitude`)) + cos(radians('.$lat.')) * cos(radians(`latitude`)) * cos(radians(abs('.$lng.' - `longitude`))))) * 69.172'), 'distance'))->from('locations'); + +[!!] You must validate or escape any user input inside of DB::expr as it will obviously not be escaped it for you. + +## Executing + +Once you are done building, you can execute the query using `execute()` and use [the results](results). + + $result = $query->execute(); + +To use a different database [config group](config) pass either the name or the config object to `execute()`. + + $result = $query->execute('config_name') \ No newline at end of file diff --git a/modules/database/guide/database/query/parameterized.md b/modules/database/guide/database/query/parameterized.md new file mode 100644 index 0000000..5a4e537 --- /dev/null +++ b/modules/database/guide/database/query/parameterized.md @@ -0,0 +1,67 @@ +# Parameterized Statements + +Using parameterized statements allows you to write SQL queries manually while still escaping the query values automatically to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Creating a query is simple: + + $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user'); + +The [DB::query] method is just a shortcut that creates a new [Database_Query] class for us, to allow method chaining. The query contains a `:user` parameter, which we will get to in a second. + +The first parameter of [DB::query] is the type of query. It should be `Database::SELECT`, `Database::INSERT`, `Database::UPDATE`, or `Database::DELETE`. This is done for compatibility reasons for drivers, and to easily determine what `execute()` should return. + +The second parameter is the query itself. Rather than trying to concatenate your query and variables together, you should make use of [Database_Query::param]. This will make your queries much easier to mantain, and will escape the values to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). + +## Parameters + +Our example query earlier contains a `:user` parameter, which we can assign to a value using [Database_Query::param] like so: + + $query->param(':user', 'john'); + +[!!] Parameter names can be any unique string, as they are replaced using [strtr](http://php.net/strtr). It is highly recommended to **not** use dollars signs as parameter names to prevent confusion. Colons are commonly used. + +You can also update the `:user` parameter by calling [Database_Query::param] again: + + $query->param(':user', $_GET['search']); + +If you want to set multiple parameters at once, you can use [Database_Query::parameters]. + + $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user AND status = :status'); + + $query->parameters(array( + ':user' => 'john', + ':status' => 'active', + )); + +It is also possible to bind a parameter to a variable, using a [variable reference]((http://php.net/language.references.whatdo)). This can be extremely useful when running the same query many times: + + $query = DB::query(Database::INSERT, 'INSERT INTO users (username, password) VALUES (:user, :pass)') + ->bind(':user', $username) + ->bind(':pass', $password); + + foreach ($new_users as $username => $password) + { + $query->execute(); + } + +In the above example, the variables `$username` and `$password` are changed for every loop of the `foreach` statement. When the parameter changes, it effectively changes the `:user` and `:pass` query parameters. Careful parameter binding can save a lot of code when it is used properly. + +The only difference between `param()` and `bind()` is that `bind()` passes the variable by reference rather than by assignment (copied), so future changes to the variable can be "seen" by the query. + +[!!] Although all parameters are escaped to prevent SQL injection, it is still a good idea to validate/sanitize your input. + +## Display the raw query + +If you want to display the SQL that will be executed, you can simply echo the query: + + echo $query; + // Should display: + // SELECT * FROM users WHERE username = 'john' + +## Executing + +Once you have assigned something to each of the parameters, you can execute the query using `execute()` and use [the results](results). + + $result = $query->execute(); + +To use a different database [config group](config) pass either the name or the config object to `execute()`. + + $result = $query->execute('config_name') \ No newline at end of file diff --git a/modules/database/guide/database/results.md b/modules/database/guide/database/results.md new file mode 100644 index 0000000..e13dfa1 --- /dev/null +++ b/modules/database/guide/database/results.md @@ -0,0 +1,105 @@ +# Results + +## Execute + +Once you have a query object built, either through a parameterized statement or through the builder, you must then `execute()` the query and retrieve the results. Depending on the query type used, the results returned will vary. + +## Select + +[DB::select] will return a [Database_Result] object which you can then iterate over. This example shows how you can iterate through the [Database_Result] using a foreach. + + $results = DB::select()->from('users')->where('verified', '=', 0)->execute(); + foreach($results as $user) + { + // Send reminder email to $user['email'] + echo $user['email']." needs to verify his/her account\n"; + } + +### Select - `as_object()` and `as_assoc()` + +When iterating over a result set, the default type will be an associative array with the column names or aliases as the keys. As an option, before calling `execute()`, you can specify to return the result rows as an object by using the `as_object()` method. The `as_object()` method takes one parameter, the name of the class of your choice, but will default to TRUE which uses the `stdClass`. Here is the example again using `stdClass`. + + $results = DB::select()->from('users')->where('verified', '=', 0)->as_object()->execute(); + foreach($results as $user) + { + // Send reminder email to $user->email + echo $user->email." needs to verify his/her account\n"; + } + +[!!] The method `as_assoc()` will remove the object name and return the results set back to an associative array. Since this is the default, this method is seldom required. + +### Select - `as_array()` + +Sometimes you will require the results as a pure array rather than as an object. The `Database_Result` method `as_array()` will return an array of all rows. + + $results = DB::select('id', 'email')->from('users')->execute(); + $users = $results->as_array(); + foreach($users as $user) + { + echo 'User ID: '.$user['id']; + echo 'User Email: '.$user['email']; + } + +It also accepts two parameters that can be very helpful: `$key` and `$value`. When passing a value to `$key` you will index the resulting array by the column specified. + + $results = DB::select('id', 'email')->from('users')->execute(); + $users = $results->as_array('id'); + foreach($users as $id => $user) + { + echo 'User ID: '.$id; + echo 'User Email: '.$user['email']; + } + +The second parameter, `$value`, will reference the column specified and return that value rather than the whole row. This is particularly useful when making `

+

+ + + +~~~ + +View for `crop/do` action goes to `views/crop/do.php`. + +~~~ + + + Upload Profile Image Result + + + +

Upload success

+

+ Here is your uploaded and cropped avatar: + " alt="Uploaded avatar" /> +

+ +

Something went wrong with the upload

+

+ + + +~~~ + +## Screenshots + +Below are screenshots for this example. + +![Original image](crop_orig.jpg) + +_Original image to upload_ + +![Upload image form](crop_form.jpg) + +_Upload image form_ + +![Upload result page](crop_result.jpg) + +_Upload result form_ \ No newline at end of file diff --git a/modules/image/guide/image/examples/dynamic.md b/modules/image/guide/image/examples/dynamic.md new file mode 100644 index 0000000..2d70260 --- /dev/null +++ b/modules/image/guide/image/examples/dynamic.md @@ -0,0 +1,108 @@ +# Dynamic Image Controller + +In this example, we have images under `/uploads` under the webroot directory. We allow the user to render any image with dynamic dimension and is resized on the fly. It also caches the response for 1 hour to show basic caching mechanism. + +## Route + +First, we need a [Route]. This [Route] is based on this URL pattern: + +`/imagefly/filename/width/height` - where filename is the name of the image without the extension. This assumes that all images are in `jpg` and all filenames uses numbers, letters, dash and underscores only. + +This is our [Route] definition: + +~~~ +/** + * Set route for image fly + */ +Route::set('imagefly', 'imagefly///', array('image' => '[-09a-zA-Z_]+', 'width' => '[0-9]+', 'height' => '[0-9]+')) + ->defaults(array( + 'controller' => 'imagefly', + 'action' => 'index' + )); +~~~ + +We ensure that the filename is only composed of letters, numbers and underscores, width and height must be numeric. + +## Controller + +Our controller simply accepts the request and capture the following parameters as defined by the [Route]: + +* `filename` - without the filename extension (and without dot) +* `width` +* `height` + +Then it finds the image file and when found, render it on the browser. Additional features added are browser caching. + +~~~ +request->param('image'); + $width = (int) $this->request->param('width'); + $height = (int) $this->request->param('height'); + + $rendered = FALSE; + if ($file AND $width AND $height) + { + $filename = DOCROOT.'uploads/'.$file.'.jpg'; + + if (is_file($filename)) + { + $this->_render_image($filename, $width, $height); + $rendered = TRUE; + } + } + + if ( ! $rendered) + { + $this->response->status(404); + } + } + + protected function _render_image($filename, $width, $height) + { + // Calculate ETag from original file padded with the dimension specs + $etag_sum = md5(base64_encode(file_get_contents($filename)).$width.','.$height); + + // Render as image and cache for 1 hour + $this->response->headers('Content-Type', 'image/jpeg') + ->headers('Cache-Control', 'max-age='.Date::HOUR.', public, must-revalidate') + ->headers('Expires', gmdate('D, d M Y H:i:s', time() + Date::HOUR).' GMT') + ->headers('Last-Modified', date('r', filemtime($filename))) + ->headers('ETag', $etag_sum); + + if ( + $this->request->headers('if-none-match') AND + (string) $this->request->headers('if-none-match') === $etag_sum) + { + $this->response->status(304) + ->headers('Content-Length', '0'); + } + else + { + $result = Image::factory($filename) + ->resize($width, $height) + ->render('jpg'); + + $this->response->body($result); + } + } +} +~~~ + +When the parameters are invalid or the filename does not exists, it simply returns 404 not found error. + +The rendering of image uses some caching mechanism. One by setting the max age and expire headers and second by using etags. + +## Screenshots + +Visiting [http://localhost/kohana/imagefly/kitteh/400/400](http://localhost/kohana/imagefly/kitteh/400/400) yields: + +![Kitten 400x400](dynamic-400.jpg) + +Visiting [http://localhost/kohana/imagefly/kitteh/600/500](http://localhost/kohana/imagefly/kitteh/600/500) yields: + +![Kitten 400x400](dynamic-600.jpg) \ No newline at end of file diff --git a/modules/image/guide/image/examples/upload.md b/modules/image/guide/image/examples/upload.md new file mode 100644 index 0000000..e08b780 --- /dev/null +++ b/modules/image/guide/image/examples/upload.md @@ -0,0 +1,139 @@ +# Upload Image + +The following example shows how to handle uploading of an image, resize it and save it to a file. Be sure you have enabled the [Image] module as discussed in getting started guide. + +Assuming you are creating a web application that allows your members to upload their profile picture (avatar), the steps below explains it how. + +## Controller + +First we need to create a controller that handles the requests for uploading an image. We will name it `Controller_Avatar` and accessible through `/avatar` URL. Assuming that your project is located at [http://localhost/kohana](http://localhost/kohana), then our avatar controller is at [http://localhost/kohana/avatar](http://localhost/kohana/avatar). + +For simplicity, the upload form will be on `index` action and `upload` action will process the uploaded file. This is what our controller now looks like. Please note that we are not using [Controller_Template], just [Controller]. + +~~~ +response->body($view); + } + + public function action_upload() + { + $view = View::factory('avatar/upload'); + $error_message = NULL; + $filename = NULL; + + if ($this->request->method() == Request::POST) + { + if (isset($_FILES['avatar'])) + { + $filename = $this->_save_image($_FILES['avatar']); + } + } + + if ( ! $filename) + { + $error_message = 'There was a problem while uploading the image. + Make sure it is uploaded and must be JPG/PNG/GIF file.'; + } + + $view->uploaded_file = $filename; + $view->error_message = $error_message; + $this->response->body($view); + } + + protected function _save_image($image) + { + if ( + ! Upload::valid($image) OR + ! Upload::not_empty($image) OR + ! Upload::type($image, array('jpg', 'jpeg', 'png', 'gif'))) + { + return FALSE; + } + + $directory = DOCROOT.'uploads/'; + + if ($file = Upload::save($image, NULL, $directory)) + { + $filename = strtolower(Text::random('alnum', 20)).'.jpg'; + + Image::factory($file) + ->resize(200, 200, Image::AUTO) + ->save($directory.$filename); + + // Delete the temporary file + unlink($file); + + return $filename; + } + + return FALSE; + } + +} +~~~ + +We have `index` and `upload` actions. `index` action will display the upload form and `upload` action will process the uploaded image and provides feedback to the user. + +In `upload` action, it checks if the request method was `POST`, then delegates the process to `_save_image()` method which in turn performs various checks and finally resize and save the image to the `uploads` directory. + +## Views + +For the upload form (the `index` action), the view is located at `views/avatar/index.php`. + +~~~ + + + Upload Avatar + + +

Upload your avatar

+
+

Choose file:

+

+

+
+ + +~~~ + +Take note of the action attribute. It points to our `avatar/upload` action whose view code goes to `views/avatar/upload.php`. + +~~~ + + + Upload Avatar Result + + + +

Upload success

+

+ Here is your uploaded avatar: + " alt="Uploaded avatar" /> +

+ +

Something went wrong with the upload

+

+ + + +~~~ + +When the upload is successfull, a success message is displayed with the uploaded image displayed. Otherwise, when it fails, it displays an error message. + +## Screenshots + +Below are screenshots for this example. + +![Upload image form](upload_form.jpg) + +_Upload image form_ + +![Upload result page](upload_result.jpg) + +_Upload result form_ \ No newline at end of file diff --git a/modules/image/guide/image/index.md b/modules/image/guide/image/index.md new file mode 100644 index 0000000..0225c26 --- /dev/null +++ b/modules/image/guide/image/index.md @@ -0,0 +1,21 @@ +# Image + +Kohana 3.x provides a simple yet powerful image manipulation module. The [Image] module provides features that allows your application to resize images, crop, rotate, flip and many more. + +## Drivers + +[Image] module ships with [Image_GD] driver which requires `GD` extension enabled in your PHP installation. This is the default driver. Additional drivers can be created by extending the [Image] class. + +## Getting Started + +Before using the image module, we must enable it first on `APPPATH/bootstrap.php`: + +~~~ +Kohana::modules(array( + ... + 'image' => MODPATH.'image', // Image manipulation + ... +)); +~~~ + +Next: [Using the image module](using). \ No newline at end of file diff --git a/modules/image/guide/image/menu.md b/modules/image/guide/image/menu.md new file mode 100644 index 0000000..8c87743 --- /dev/null +++ b/modules/image/guide/image/menu.md @@ -0,0 +1,6 @@ +## [Image]() +- [Using](using) +- [Examples](examples) + - [Upload Image](examples/upload) + - [Crop Profile Image](examples/crop) + - [Dynamic Image Controller](examples/dynamic) \ No newline at end of file diff --git a/modules/image/guide/image/using.md b/modules/image/guide/image/using.md new file mode 100644 index 0000000..fd1859b --- /dev/null +++ b/modules/image/guide/image/using.md @@ -0,0 +1,112 @@ +# Basic Usage + +Shown here are the basic usage of this module. For full documentation about the image module usage, visit the [Image] api browser. + +## Creating Instance + +[Image::factory()] creates an instance of the image object and prepares it for manipulation. It accepts the `filename` as an arguement and an optional `driver` parameter. When `driver` is not specified, the default driver `GD` is used. + +~~~ +// Uses the image from upload directory +$img = Image::factory(DOCROOT.'uploads/sample-image.jpg'); +~~~ + +Once an instance is created, you can now manipulate the image by using the following instance methods. + +## Resize + +Resize the image to the given size. Either the width or the height can be omitted and the image will be resized proportionally. + +Using the image object above, we can resize our image to say 150x150 pixels with automatic scaling using the code below: + +~~~ +$img->resize(150, 150, Image::AUTO); +~~~ + +The parameters are `width`, `height` and `master` dimension respectively. With `AUTO` master dimension, the image is resized by either width or height depending on which is closer to the specified dimension. + +Other examples: + +~~~ +// Resize to 200 pixels on the shortest side +$img->resize(200, 200); + +// Resize to 200x200 pixels, keeping aspect ratio +$img->resize(200, 200, Image::INVERSE); + +// Resize to 500 pixel width, keeping aspect ratio +$img->resize(500, NULL); + +// Resize to 500 pixel height, keeping aspect ratio +$img->resize(NULL, 500); + +// Resize to 200x500 pixels, ignoring aspect ratio +$img->resize(200, 500, Image::NONE); +~~~ + +## Render + +You can render the image object directly to the browser using the [Image::render()] method. + +~~~ +$img = Image::factory(DOCROOT.'uploads/colorado-farm-1920x1200.jpg'); + +header('Content-Type: image/jpeg'); + +echo $img->resize(300, 300) + ->render(); +~~~ + +What it did is resize a 1920x1200 wallpaper image into 300x300 proportionally and render it to the browser. If you are trying to render the image in a controller action, you can do instead: + +~~~ +$img = Image::factory(DOCROOT.'uploads/colorado-farm-1920x1200.jpg'); + +$this->response->headers('Content-Type', 'image/jpg'); + +$this->response->body( + $img->resize(300, 300) + ->render() +); +~~~ + +[Image::render()] method also allows you to specify the type and quality of the rendered image. + +~~~ +// Render the image at 50% quality +$img->render(NULL, 50); + +// Render the image as a PNG +$img->render('png'); +~~~ + +## Save To File + +[Image::save()] let's you save the image object to a file. It has two parameters: `filename` and `quality`. If `filename` is omitted, the original file used will be overwritten instead. The `quality` parameter is an integer from 1-100 which indicates the quality of image to save which defaults to 100. + +On our example above, instead of rendering the file to the browser, you may want to save it somewhere instead. To do so, you may: + +~~~ +$img = Image::factory(DOCROOT.'uploads/colorado-farm-1920x1200.jpg'); + +$filename = DOCROOT.'uploads/img-'.uniqid().'.jpg'; + +$img->resize(300, 300) + ->save($filename, 80); +~~~ + +What we do is resize the image and save it to file reducing quality to 80% and save it to the upload directory using a unique filename. + +## Other Methods + +There are more methods available for the [Image] module which provides powerfull features that are best describe in the API documentation. Here are some of them: + +* [Image::background()] - Set the background color of an image. +* [Image::crop()] - Crop an image to the given size. +* [Image::flip()] - Flip the image along the horizontal or vertical axis. +* [Image::reflection()] - Add a reflection to an image. +* [Image::rotate()] - Rotate the image by a given amount. +* [Image::sharpen()] - Sharpen the image by a given amount. +* [Image::watermark()] - Add a watermark to an image with a specified opacity. + +Next: [Examples](examples) \ No newline at end of file diff --git a/modules/image/media/guide/image/Thumbs.db b/modules/image/media/guide/image/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..a0f028f80c3d119e1f50f6f3ab445a267420816c GIT binary patch literal 19968 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
jmD(tbYq_^*NB)Il%NP+ ze;`#>fBzNn{bHcVcltM=80*$8pPTz3VQd4L-ir}hI!c&4D1suVJKRffzNR=Ef1g8U z(c3tOx2-lBVfNpJ*Bv-3Sy=L|d>Q@jF;kSoW8~RjIFoi{-{>dY&qzM&D%f zV|RBvYmkAs7OT#}m5}v{6HK%;1qs@gi?C7=fq=E_Oo3YXg#HDOs{di&tElYvxnBka zb226de$wqc+l%%|j%f9vjI}frF5r8|^yczc6+Ija>z+u(mXt$VqOmLEVs6yMzjy2r zOlW2#BjG7MM(fK68C(#crv8Gb9K5F9Ze!QcY3 zL=dSdv>?=_d5u*0uzZv8un9?@I8c!TO%`U=c$_RkFb@Fs_kv867uI8;OBY6O?9tgK zb_a{TEg;j^cT?5)jgJqVzW9mfAks=FW@Dj?j5Z(0lFae*RATo9Nl#J*5L3LmV~Mg8 zd1XziAkpD1>tai#Q9VnZlL^T&B6|VhD$owCp}qQ%4pmhRL@GlU=7)p9MWY+6*eFb( z7XSd;T$z^LpeVj0*^l|@R0PztQp$wWO+^UQbdW|_*_3b{o#1j;;GE;hnR@le|DpR3 zk+42;(*H{)q&+g2G-L)ix;Bf?O3=kzc6s{gVLxzYs|b&6ftIk~vPz~f7>DCghYj01 zS_pW)K9X}~8}M*_Iq}w7P7dZW9WaMu66?|3+5!f%s6MX!z?Q>|8I&xIz1XcOd(wX_ zyB-(Zbo!dH76P^@oH!>bo5|NO9ha4}G0QGMDxg~yGEYb=K&>t41bKo6FQ#P6Y}WSe ztz@NR0h@fapcEw=ZUPr66b?o^ZvY@wg~xywAcYyAzJgS literal 0 HcmV?d00001 diff --git a/modules/image/media/guide/image/crop_orig.jpg b/modules/image/media/guide/image/crop_orig.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1389acc3f6954c66a7abeb0ddd587960d12b4926 GIT binary patch literal 31360 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 literal 0 HcmV?d00001 diff --git a/modules/image/media/guide/image/dynamic-400.jpg b/modules/image/media/guide/image/dynamic-400.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13b691f07b876985a4227afd62a821629adbe68f GIT binary patch literal 27609 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 literal 0 HcmV?d00001 diff --git a/modules/image/media/guide/image/dynamic-600.jpg b/modules/image/media/guide/image/dynamic-600.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f521b49b14679d954038f36b2c2048176ccd2060 GIT binary patch literal 45517 zcma&NbzB@x7bc2_;4XncaCaL#!QE|e7#xC=z+k~0GPt|Dy99UlK=8rcJ^3AhcWy0hQPwZ;3L2zp`gRSaHhk+h=O5YZ0liQYDWLAzy!h|As`?i zA|N3mBE3g?OQ^_5NXV#YC@82XC}>z{{{}2H3`{Ic3^ZI^JUm=nQW6ppQu6;5VBfua zhmM9$goQ=)0UsOx!~d%I|C{;S1A~nWs{&^R4~q>0hYbsl4g0qbW)B7i4jvX3_Kp94 z13UsO93l+TyEpLP@IUdl0m8u}AR=MGz`?;Hz#zgQ!NDT_tN4avV^QEBeBz+Q#rurN zuJVPG3f~w>%<%`8H~}>`_S=<#MMOYGfPaSo|4(bMFxYSuSR7w);6JIFaI$}Pf=pB5 za{1-tBGib@sGN7JnZ{B%i{o)`Uijm@b&81izbU}LV)?-sSD%0R^b;F0jSKt61r8P- z?q9P1X_x=3q6DL0i`{(nVn(pa~O%>z6zhThe--NN@vERCXw8H7eVAR=U!sDgc zrX2`2rw_~h;v6&q*pB|R9R}DArQQx@)Y(${w50^_S9(ML&!}2mwrRF#3|k11+$k?+=&9s;+}LBJC+o0<%Eo19M9bNH0-!;(%;fE|Go zLO79B(_z7tr?!(=yIfyP_VNGhV>?jNj=B(#;}W@oWGlzTr@ACmf&78;oj6`T&XD@3 zEj}^x7e^~eBDGJS%1x|3GBIn4B>-w<(rC
| \z + ) + ) + }xm', + array(&$this, '_processDefListItems_callback_dd'), $list_str); + + return $list_str; + } + function _processDefListItems_callback_dt($matches) { + $terms = explode("\n", trim($matches[1])); + $text = ''; + foreach ($terms as $term) { + $term = $this->runSpanGamut(trim($term)); + $text .= "\n
" . $term . "
"; + } + return $text . "\n"; + } + function _processDefListItems_callback_dd($matches) { + $leading_line = $matches[1]; + $marker_space = $matches[2]; + $def = $matches[3]; + + if ($leading_line || preg_match('/\n{2,}/', $def)) { + # Replace marker with the appropriate whitespace indentation + $def = str_repeat(' ', strlen($marker_space)) . $def; + $def = $this->runBlockGamut($this->outdent($def . "\n\n")); + $def = "\n". $def ."\n"; + } + else { + $def = rtrim($def); + $def = $this->runSpanGamut($this->outdent($def)); + } + + return "\n
" . $def . "
\n"; + } + + + function doFencedCodeBlocks($text) { + # + # Adding the fenced code block syntax to regular Markdown: + # + # ~~~ + # Code block + # ~~~ + # + $less_than_tab = $this->tab_width; + + $text = preg_replace_callback('{ + (?:\n|\A) + # 1: Opening marker + ( + ~{3,} # Marker: three tilde or more. + ) + [ ]* \n # Whitespace and newline following marker. + + # 2: Content + ( + (?> + (?!\1 [ ]* \n) # Not a closing marker. + .*\n+ + )+ + ) + + # Closing marker. + \1 [ ]* \n + }xm', + array(&$this, '_doFencedCodeBlocks_callback'), $text); + + return $text; + } + function _doFencedCodeBlocks_callback($matches) { + $codeblock = $matches[2]; + $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); + $codeblock = preg_replace_callback('/^\n+/', + array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock); + $codeblock = "
$codeblock
"; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + function _doFencedCodeBlocks_newlines($matches) { + return str_repeat("empty_element_suffix", + strlen($matches[0])); + } + + + # + # Redefining emphasis markers so that emphasis by underscore does not + # work in the middle of a word. + # + var $em_relist = array( + '' => '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + $value = trim($this->runSpanGamut($value)); + + # Check if this should be enclosed in a paragraph. + # Clean tag hashes & block tag hashes are left alone. + $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); + + if ($is_p) { + $value = "

$value

"; + } + $grafs[$key] = $value; + } + + # Join grafs in one text, then unhash HTML tags. + $text = implode("\n\n", $grafs); + + # Finish by removing any tag hashes still present in $text. + $text = $this->unhash($text); + + return $text; + } + + + ### Footnotes + + function stripFootnotes($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [^id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1 + [ ]* + \n? # maybe *one* newline + ( # text = $2 (no blank lines allowed) + (?: + .+ # actual text + | + \n # newlines but + (?!\[\^.+?\]:\s)# negative lookahead for footnote marker. + (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed + # by non-indented content + )* + ) + }xm', + array(&$this, '_stripFootnotes_callback'), + $text); + return $text; + } + function _stripFootnotes_callback($matches) { + $note_id = $this->fn_id_prefix . $matches[1]; + $this->footnotes[$note_id] = $this->outdent($matches[2]); + return ''; # String that will replace the block + } + + + function doFootnotes($text) { + # + # Replace footnote references in $text [^id] with a special text-token + # which will be replaced by the actual footnote marker in appendFootnotes. + # + if (!$this->in_anchor) { + $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text); + } + return $text; + } + + + function appendFootnotes($text) { + # + # Append footnote list to text. + # + $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $text); + + if (!empty($this->footnotes_ordered)) { + $text .= "\n\n"; + $text .= "
\n"; + $text .= "fn_backlink_class != "") { + $class = $this->fn_backlink_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_backlink_title != "") { + $title = $this->fn_backlink_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + $num = 0; + + while (!empty($this->footnotes_ordered)) { + $footnote = reset($this->footnotes_ordered); + $note_id = key($this->footnotes_ordered); + unset($this->footnotes_ordered[$note_id]); + + $footnote .= "\n"; # Need to append newline before parsing. + $footnote = $this->runBlockGamut("$footnote\n"); + $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $footnote); + + $attr = str_replace("%%", ++$num, $attr); + $note_id = $this->encodeAttribute($note_id); + + # Add backlink to last paragraph; create new paragraph if needed. + $backlink = ""; + if (preg_match('{

$}', $footnote)) { + $footnote = substr($footnote, 0, -4) . " $backlink

"; + } else { + $footnote .= "\n\n

$backlink

"; + } + + $text .= "
  • \n"; + $text .= $footnote . "\n"; + $text .= "
  • \n\n"; + } + + $text .= "\n"; + $text .= "
    "; + } + return $text; + } + function _appendFootnotes_callback($matches) { + $node_id = $this->fn_id_prefix . $matches[1]; + + # Create footnote marker only if it has a corresponding footnote *and* + # the footnote hasn't been used by another marker. + if (isset($this->footnotes[$node_id])) { + # Transfert footnote content to the ordered list. + $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id]; + unset($this->footnotes[$node_id]); + + $num = $this->footnote_counter++; + $attr = " rel=\"footnote\""; + if ($this->fn_link_class != "") { + $class = $this->fn_link_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_link_title != "") { + $title = $this->fn_link_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + + $attr = str_replace("%%", $num, $attr); + $node_id = $this->encodeAttribute($node_id); + + return + "". + "$num". + ""; + } + + return "[^".$matches[1]."]"; + } + + + ### Abbreviations ### + + function stripAbbreviations($text) { + # + # Strips abbreviations from text, stores titles in hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [id]*: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1 + (.*) # text = $2 (no blank lines allowed) + }xm', + array(&$this, '_stripAbbreviations_callback'), + $text); + return $text; + } + function _stripAbbreviations_callback($matches) { + $abbr_word = $matches[1]; + $abbr_desc = $matches[2]; + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + return ''; # String that will replace the block + } + + + function doAbbreviations($text) { + # + # Find defined abbreviations in text and wrap them in elements. + # + if ($this->abbr_word_re) { + // cannot use the /x modifier because abbr_word_re may + // contain significant spaces: + $text = preg_replace_callback('{'. + '(?abbr_word_re.')'. + '(?![\w\x1A])'. + '}', + array(&$this, '_doAbbreviations_callback'), $text); + } + return $text; + } + function _doAbbreviations_callback($matches) { + $abbr = $matches[0]; + if (isset($this->abbr_desciptions[$abbr])) { + $desc = $this->abbr_desciptions[$abbr]; + if (empty($desc)) { + return $this->hashPart("$abbr"); + } else { + $desc = $this->encodeAttribute($desc); + return $this->hashPart("$abbr"); + } + } else { + return $matches[0]; + } + } + +} + + +/* + +PHP Markdown Extra +================== + +Description +----------- + +This is a PHP port of the original Markdown formatter written in Perl +by John Gruber. This special "Extra" version of PHP Markdown features +further enhancements to the syntax for making additional constructs +such as tables and definition list. + +Markdown is a text-to-HTML filter; it translates an easy-to-read / +easy-to-write structured text format into HTML. Markdown's text format +is most similar to that of plain text email, and supports features such +as headers, *emphasis*, code blocks, blockquotes, and links. + +Markdown's syntax is designed not as a generic markup language, but +specifically to serve as a front-end to (X)HTML. You can use span-level +HTML tags anywhere in a Markdown document, and you can use block level +HTML tags (like
    and as well). + +For more information about Markdown's syntax, see: + + + + +Bugs +---- + +To file bug reports please send email to: + + + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output Markdown actually produced. + + +Version History +--------------- + +See the readme file for detailed release notes for this version. + + +Copyright and License +--------------------- + +PHP Markdown & Extra +Copyright (c) 2004-2008 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2006 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + +*/ +?> \ No newline at end of file diff --git a/modules/userguide/views/userguide/api/class.php b/modules/userguide/views/userguide/api/class.php new file mode 100644 index 0000000..08afd9d --- /dev/null +++ b/modules/userguide/views/userguide/api/class.php @@ -0,0 +1,124 @@ +

    + modifiers, $doc->class->name ?> + parents as $parent): ?> +
    extends uri(array('class' => $parent->name)), $parent->name, NULL, NULL, TRUE) ?> + +

    + +class->getInterfaceNames()): ?> +

    +Implements: +uri(array('class' => $interfaces[$i])), $interfaces[$i], NULL, NULL, TRUE); +} +?> +

    + + +is_transparent($doc->class->name)):?> +

    +This class is a transparent base class for uri(array('class'=>$child)),$child) ?> and +should not be accessed directly. +

    + + +description() ?> + +tags): ?> +
    +tags() as $name => $set): ?> +
    + +
    + + +
    + + +

    +class->getFilename()): ?> +Class declared in on line class->getStartLine() ?>. + +Class is not declared in a file, it is probably an internal class->name).'.php', 'PHP class') ?>. + +

    + +
    +
    +

    +
      + constants): ?> + constants as $name => $value): ?> +
    • + + +
    • + +
    +
    +
    +

    + +
    +
    +

    + +
    +
    + +
    + +constants): ?> +
    +

    +
    +constants() as $name => $value): ?> +

    +
    + +
    +
    + + +properties()): ?> +

    +
    +
    + +

    modifiers ?> type ?> $property->name ?>

    +
    description ?>
    +
    value ?>
    +default !== $prop->value): ?> +

    default ?>
    + + +
    +
    + + +methods()): ?> +

    +
    + +set('doc', $method)->set('route', $route) ?> + +
    + diff --git a/modules/userguide/views/userguide/api/menu.php b/modules/userguide/views/userguide/api/menu.php new file mode 100644 index 0000000..4f4f22f --- /dev/null +++ b/modules/userguide/views/userguide/api/menu.php @@ -0,0 +1,18 @@ + +

    Modules

    + \ No newline at end of file diff --git a/modules/userguide/views/userguide/api/method.php b/modules/userguide/views/userguide/api/method.php new file mode 100644 index 0000000..3dcd332 --- /dev/null +++ b/modules/userguide/views/userguide/api/method.php @@ -0,0 +1,45 @@ +
    + +method->getDeclaringClass(); ?> +

    + modifiers, $doc->method->name ?>( params ? $doc->params_short() : '' ?>) + (defined in uri(array('class' => $declares->name)), $declares->name, NULL, NULL, TRUE) ?>) +

    + +
    +description ?> +
    + +params): ?> +

    Parameters

    +
      +params as $param): ?> +
    • +reference?'byref ':'').($param->type?$param->type:'unknown') ?> +name ?> +default?' = '.$param->default.'':'required' ?> +description?' - '.$param->description:'' ?> +
    • + +
    + + +tags) echo View::factory('userguide/api/tags')->set('tags', $doc->tags) ?> + +return): ?> +

    +
      +return as $set): list($type, $text) = $set; ?> +
    • + +
    + + +source): ?> +
    +

    +
    source) ?>
    +
    + + +
    diff --git a/modules/userguide/views/userguide/api/tags.php b/modules/userguide/views/userguide/api/tags.php new file mode 100644 index 0000000..18779ac --- /dev/null +++ b/modules/userguide/views/userguide/api/tags.php @@ -0,0 +1,6 @@ +

    Tags

    +
      + $set): ?> +
    • + +
    \ No newline at end of file diff --git a/modules/userguide/views/userguide/api/toc.php b/modules/userguide/views/userguide/api/toc.php new file mode 100644 index 0000000..88075d9 --- /dev/null +++ b/modules/userguide/views/userguide/api/toc.php @@ -0,0 +1,69 @@ +

    + + + + + + +
    + + $methods): $link = $route->uri(array('class' => $class)) ?> +
    +

    +
      + +
    • + +
    +
    + + +
    diff --git a/modules/userguide/views/userguide/error.php b/modules/userguide/views/userguide/error.php new file mode 100644 index 0000000..1fb7cbb --- /dev/null +++ b/modules/userguide/views/userguide/error.php @@ -0,0 +1,3 @@ +

    Kodoc -

    + +

    \ No newline at end of file diff --git a/modules/userguide/views/userguide/examples/error.php b/modules/userguide/views/userguide/examples/error.php new file mode 100644 index 0000000..81548ce --- /dev/null +++ b/modules/userguide/views/userguide/examples/error.php @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/modules/userguide/views/userguide/examples/hello_world_error.php b/modules/userguide/views/userguide/examples/hello_world_error.php new file mode 100644 index 0000000..d5dcf44 --- /dev/null +++ b/modules/userguide/views/userguide/examples/hello_world_error.php @@ -0,0 +1,696 @@ + + +
    +

    Kohana_View_Exception [ 0 ]: The requested view site could not be found

    +
    +

    SYSPATH/classes/kohana/view.php [ 215 ]

    +
    210 	 */
    +
    +211 	public function set_filename($file)
    +212 	{
    +213 		if (($path = Kohana::find_file('views', $file)) === FALSE)
    +214 		{
    +215 			throw new Kohana_View_Exception('The requested view :file could not be found', array(
    +216 				':file' => $file,
    +
    +217 			));
    +218 		}
    +219 
    +220 		// Store the file path locally
    +
      +
    1. +

      + + + SYSPATH/classes/kohana/view.php [ 115 ] + + » + Kohana_View->set_filename(arguments) +

      +
    + + + + + +
    file
    string(4) "site"
    +
    + + + + +
  • +

    + + SYSPATH/classes/kohana/view.php [ 26 ] + + » + Kohana_View->__construct(arguments) +

    + + + +
  • +
  • +

    + + + SYSPATH/classes/kohana/controller/template.php [ 32 ] + + » + Kohana_View::factory(arguments) +

    + + + +
  • +
  • +

    + + {PHP internal call} + + » + Kohana_Controller_Template->before() +

    + +
  • +
  • +

    + + SYSPATH/classes/kohana/request.php [ 840 ] + + » + ReflectionMethod->invoke(arguments) +

    + + + +
  • +
  • +

    + + APPPATH/bootstrap.php [ 76 ] + + + » + Kohana_Request->execute() +

    + +
  • +
  • +

    + + DOCROOT/index.php [ 106 ] + + » + require(arguments) +

    + + + +
  • + + +

    Environment

    + + diff --git a/modules/userguide/views/userguide/index.php b/modules/userguide/views/userguide/index.php new file mode 100644 index 0000000..5764fcb --- /dev/null +++ b/modules/userguide/views/userguide/index.php @@ -0,0 +1,20 @@ +

    User Guide

    + +

    The following modules have userguide pages:

    + + + + $options): ?> + +

    + uri(array('module' => $url)), $options['name'], NULL, NULL, TRUE) ?> - + +

    + + + + + +

    I couldn't find any modules with userguide pages.

    + + \ No newline at end of file diff --git a/modules/userguide/views/userguide/menu.php b/modules/userguide/views/userguide/menu.php new file mode 100644 index 0000000..e6537c9 --- /dev/null +++ b/modules/userguide/views/userguide/menu.php @@ -0,0 +1,17 @@ +

    Modules

    + + + +
      + $options): ?> + +
    • uri(array('module' => $url)), $options['name'], NULL, NULL, TRUE) ?>
    • + + +
    + + + +

    No modules.

    + + \ No newline at end of file diff --git a/modules/userguide/views/userguide/page-toc.php b/modules/userguide/views/userguide/page-toc.php new file mode 100644 index 0000000..4eefcc2 --- /dev/null +++ b/modules/userguide/views/userguide/page-toc.php @@ -0,0 +1,10 @@ + +
    + + 1): ?> + + +
    + +
    + diff --git a/modules/userguide/views/userguide/template.php b/modules/userguide/views/userguide/template.php new file mode 100644 index 0000000..d94efc4 --- /dev/null +++ b/modules/userguide/views/userguide/template.php @@ -0,0 +1,110 @@ + + + + + +<?php echo $title ?> | Kohana <?php echo 'User Guide'; ?> + + $media) echo HTML::style($style, array('media' => $media), NULL, TRUE), "\n" ?> + + + + + + + +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
      + $title): ?> + +
    • + +
    • + + +
    +
    +
    +
    + +
    +
    +
    + + + +
    + + + Documentation comments powered by Disqus + +
    +
    +
    +
    + + + + + + + + diff --git a/system/classes/Arr.php b/system/classes/Arr.php new file mode 100644 index 0000000..376cb97 --- /dev/null +++ b/system/classes/Arr.php @@ -0,0 +1,3 @@ + 'john.doe')); + * + * // Returns FALSE + * Arr::is_assoc('foo', 'bar'); + * + * @param array $array array to check + * @return boolean + */ + public static function is_assoc(array $array) + { + // Keys of the array + $keys = array_keys($array); + + // If the array keys of the keys match the keys, then the array must + // not be associative (e.g. the keys array looked like {0:0, 1:1...}). + return array_keys($keys) !== $keys; + } + + /** + * Test if a value is an array with an additional check for array-like objects. + * + * // Returns TRUE + * Arr::is_array(array()); + * Arr::is_array(new ArrayObject); + * + * // Returns FALSE + * Arr::is_array(FALSE); + * Arr::is_array('not an array!'); + * Arr::is_array(Database::instance()); + * + * @param mixed $value value to check + * @return boolean + */ + public static function is_array($value) + { + if (is_array($value)) + { + // Definitely an array + return TRUE; + } + else + { + // Possibly a Traversable object, functionally the same as an array + return (is_object($value) AND $value instanceof Traversable); + } + } + + /** + * Gets a value from an array using a dot separated path. + * + * // Get the value of $array['foo']['bar'] + * $value = Arr::path($array, 'foo.bar'); + * + * Using a wildcard "*" will search intermediate arrays and return an array. + * + * // Get the values of "color" in theme + * $colors = Arr::path($array, 'theme.*.color'); + * + * // Using an array of keys + * $colors = Arr::path($array, array('theme', '*', 'color')); + * + * @param array $array array to search + * @param mixed $path key path string (delimiter separated) or array of keys + * @param mixed $default default value if the path is not set + * @param string $delimiter key path delimiter + * @return mixed + */ + public static function path($array, $path, $default = NULL, $delimiter = NULL) + { + if ( ! Arr::is_array($array)) + { + // This is not an array! + return $default; + } + + if (is_array($path)) + { + // The path has already been separated into keys + $keys = $path; + } + else + { + if (array_key_exists($path, $array)) + { + // No need to do extra processing + return $array[$path]; + } + + if ($delimiter === NULL) + { + // Use the default delimiter + $delimiter = Arr::$delimiter; + } + + // Remove starting delimiters and spaces + $path = ltrim($path, "{$delimiter} "); + + // Remove ending delimiters, spaces, and wildcards + $path = rtrim($path, "{$delimiter} *"); + + // Split the keys by delimiter + $keys = explode($delimiter, $path); + } + + do + { + $key = array_shift($keys); + + if (ctype_digit($key)) + { + // Make the key an integer + $key = (int) $key; + } + + if (isset($array[$key])) + { + if ($keys) + { + if (Arr::is_array($array[$key])) + { + // Dig down into the next part of the path + $array = $array[$key]; + } + else + { + // Unable to dig deeper + break; + } + } + else + { + // Found the path requested + return $array[$key]; + } + } + elseif ($key === '*') + { + // Handle wildcards + + $values = array(); + foreach ($array as $arr) + { + if ($value = Arr::path($arr, implode('.', $keys))) + { + $values[] = $value; + } + } + + if ($values) + { + // Found the values requested + return $values; + } + else + { + // Unable to dig deeper + break; + } + } + else + { + // Unable to dig deeper + break; + } + } + while ($keys); + + // Unable to find the value requested + return $default; + } + + /** + * Set a value on an array by path. + * + * @see Arr::path() + * @param array $array Array to update + * @param string $path Path + * @param mixed $value Value to set + * @param string $delimiter Path delimiter + */ + public static function set_path( & $array, $path, $value, $delimiter = NULL) + { + if ( ! $delimiter) + { + // Use the default delimiter + $delimiter = Arr::$delimiter; + } + + // Split the keys by delimiter + $keys = explode($delimiter, $path); + + // Set current $array to inner-most array path + while (count($keys) > 1) + { + $key = array_shift($keys); + + if (ctype_digit($key)) + { + // Make the key an integer + $key = (int) $key; + } + + if ( ! isset($array[$key])) + { + $array[$key] = array(); + } + + $array = & $array[$key]; + } + + // Set key on inner-most array + $array[array_shift($keys)] = $value; + } + + /** + * Fill an array with a range of numbers. + * + * // Fill an array with values 5, 10, 15, 20 + * $values = Arr::range(5, 20); + * + * @param integer $step stepping + * @param integer $max ending number + * @return array + */ + public static function range($step = 10, $max = 100) + { + if ($step < 1) + return array(); + + $array = array(); + for ($i = $step; $i <= $max; $i += $step) + { + $array[$i] = $i; + } + + return $array; + } + + /** + * Retrieve a single key from an array. If the key does not exist in the + * array, the default value will be returned instead. + * + * // Get the value "username" from $_POST, if it exists + * $username = Arr::get($_POST, 'username'); + * + * // Get the value "sorting" from $_GET, if it exists + * $sorting = Arr::get($_GET, 'sorting'); + * + * @param array $array array to extract from + * @param string $key key name + * @param mixed $default default value + * @return mixed + */ + public static function get($array, $key, $default = NULL) + { + return isset($array[$key]) ? $array[$key] : $default; + } + + /** + * Retrieves multiple paths from an array. If the path does not exist in the + * array, the default value will be added instead. + * + * // Get the values "username", "password" from $_POST + * $auth = Arr::extract($_POST, array('username', 'password')); + * + * // Get the value "level1.level2a" from $data + * $data = array('level1' => array('level2a' => 'value 1', 'level2b' => 'value 2')); + * Arr::extract($data, array('level1.level2a', 'password')); + * + * @param array $array array to extract paths from + * @param array $paths list of path + * @param mixed $default default value + * @return array + */ + public static function extract($array, array $paths, $default = NULL) + { + $found = array(); + foreach ($paths as $path) + { + Arr::set_path($found, $path, Arr::path($array, $path, $default)); + } + + return $found; + } + + /** + * Retrieves muliple single-key values from a list of arrays. + * + * // Get all of the "id" values from a result + * $ids = Arr::pluck($result, 'id'); + * + * [!!] A list of arrays is an array that contains arrays, eg: array(array $a, array $b, array $c, ...) + * + * @param array $array list of arrays to check + * @param string $key key to pluck + * @return array + */ + public static function pluck($array, $key) + { + $values = array(); + + foreach ($array as $row) + { + if (isset($row[$key])) + { + // Found a value in this row + $values[] = $row[$key]; + } + } + + return $values; + } + + /** + * Adds a value to the beginning of an associative array. + * + * // Add an empty value to the start of a select list + * Arr::unshift($array, 'none', 'Select a value'); + * + * @param array $array array to modify + * @param string $key array key name + * @param mixed $val array value + * @return array + */ + public static function unshift( array & $array, $key, $val) + { + $array = array_reverse($array, TRUE); + $array[$key] = $val; + $array = array_reverse($array, TRUE); + + return $array; + } + + /** + * Recursive version of [array_map](http://php.net/array_map), applies one or more + * callbacks to all elements in an array, including sub-arrays. + * + * // Apply "strip_tags" to every element in the array + * $array = Arr::map('strip_tags', $array); + * + * // Apply $this->filter to every element in the array + * $array = Arr::map(array(array($this,'filter')), $array); + * + * // Apply strip_tags and $this->filter to every element + * $array = Arr::map(array('strip_tags',array($this,'filter')), $array); + * + * [!!] Because you can pass an array of callbacks, if you wish to use an array-form callback + * you must nest it in an additional array as above. Calling Arr::map(array($this,'filter'), $array) + * will cause an error. + * [!!] Unlike `array_map`, this method requires a callback and will only map + * a single array. + * + * @param mixed $callbacks array of callbacks to apply to every element in the array + * @param array $array array to map + * @param array $keys array of keys to apply to + * @return array + */ + public static function map($callbacks, $array, $keys = NULL) + { + foreach ($array as $key => $val) + { + if (is_array($val)) + { + $array[$key] = Arr::map($callbacks, $array[$key]); + } + elseif ( ! is_array($keys) OR in_array($key, $keys)) + { + if (is_array($callbacks)) + { + foreach ($callbacks as $cb) + { + $array[$key] = call_user_func($cb, $array[$key]); + } + } + else + { + $array[$key] = call_user_func($callbacks, $array[$key]); + } + } + } + + return $array; + } + + /** + * Recursively merge two or more arrays. Values in an associative array + * overwrite previous values with the same key. Values in an indexed array + * are appended, but only when they do not already exist in the result. + * + * Note that this does not work the same as [array_merge_recursive](http://php.net/array_merge_recursive)! + * + * $john = array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane')); + * $mary = array('name' => 'mary', 'children' => array('jane')); + * + * // John and Mary are married, merge them together + * $john = Arr::merge($john, $mary); + * + * // The output of $john will now be: + * array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane')) + * + * @param array $array1 initial array + * @param array $array2,... array to merge + * @return array + */ + public static function merge($array1, $array2) + { + if (Arr::is_assoc($array2)) + { + foreach ($array2 as $key => $value) + { + if (is_array($value) + AND isset($array1[$key]) + AND is_array($array1[$key]) + ) + { + $array1[$key] = Arr::merge($array1[$key], $value); + } + else + { + $array1[$key] = $value; + } + } + } + else + { + foreach ($array2 as $value) + { + if ( ! in_array($value, $array1, TRUE)) + { + $array1[] = $value; + } + } + } + + if (func_num_args() > 2) + { + foreach (array_slice(func_get_args(), 2) as $array2) + { + if (Arr::is_assoc($array2)) + { + foreach ($array2 as $key => $value) + { + if (is_array($value) + AND isset($array1[$key]) + AND is_array($array1[$key]) + ) + { + $array1[$key] = Arr::merge($array1[$key], $value); + } + else + { + $array1[$key] = $value; + } + } + } + else + { + foreach ($array2 as $value) + { + if ( ! in_array($value, $array1, TRUE)) + { + $array1[] = $value; + } + } + } + } + } + + return $array1; + } + + /** + * Overwrites an array with values from input arrays. + * Keys that do not exist in the first array will not be added! + * + * $a1 = array('name' => 'john', 'mood' => 'happy', 'food' => 'bacon'); + * $a2 = array('name' => 'jack', 'food' => 'tacos', 'drink' => 'beer'); + * + * // Overwrite the values of $a1 with $a2 + * $array = Arr::overwrite($a1, $a2); + * + * // The output of $array will now be: + * array('name' => 'jack', 'mood' => 'happy', 'food' => 'tacos') + * + * @param array $array1 master array + * @param array $array2 input arrays that will overwrite existing values + * @return array + */ + public static function overwrite($array1, $array2) + { + foreach (array_intersect_key($array2, $array1) as $key => $value) + { + $array1[$key] = $value; + } + + if (func_num_args() > 2) + { + foreach (array_slice(func_get_args(), 2) as $array2) + { + foreach (array_intersect_key($array2, $array1) as $key => $value) + { + $array1[$key] = $value; + } + } + } + + return $array1; + } + + /** + * Creates a callable function and parameter list from a string representation. + * Note that this function does not validate the callback string. + * + * // Get the callback function and parameters + * list($func, $params) = Arr::callback('Foo::bar(apple,orange)'); + * + * // Get the result of the callback + * $result = call_user_func_array($func, $params); + * + * @param string $str callback string + * @return array function, params + */ + public static function callback($str) + { + // Overloaded as parts are found + $command = $params = NULL; + + // command[param,param] + if (preg_match('/^([^\(]*+)\((.*)\)$/', $str, $match)) + { + // command + $command = $match[1]; + + if ($match[2] !== '') + { + // param,param + $params = preg_split('/(? array('one' => 'something'), 'two' => 'other'); + * + * // Flatten the array + * $array = Arr::flatten($array); + * + * // The array will now be + * array('one' => 'something', 'two' => 'other'); + * + * [!!] The keys of array values will be discarded. + * + * @param array $array array to flatten + * @return array + * @since 3.0.6 + */ + public static function flatten($array) + { + $is_assoc = Arr::is_assoc($array); + + $flat = array(); + foreach ($array as $key => $value) + { + if (is_array($value)) + { + $flat = array_merge($flat, Arr::flatten($value)); + } + else + { + if ($is_assoc) + { + $flat[$key] = $value; + } + else + { + $flat[] = $value; + } + } + } + return $flat; + } + +} // End arr diff --git a/system/classes/Kohana/Config.php b/system/classes/Kohana/Config.php new file mode 100644 index 0000000..8a9798d --- /dev/null +++ b/system/classes/Kohana/Config.php @@ -0,0 +1,192 @@ +attach($reader); // Try first + * $config->attach($reader, FALSE); // Try last + * + * @param Kohana_Config_Source $source instance + * @param boolean $first add the reader as the first used object + * @return $this + */ + public function attach(Kohana_Config_Source $source, $first = TRUE) + { + if ($first === TRUE) + { + // Place the log reader at the top of the stack + array_unshift($this->_sources, $source); + } + else + { + // Place the reader at the bottom of the stack + $this->_sources[] = $source; + } + + // Clear any cached _groups + $this->_groups = array(); + + return $this; + } + + /** + * Detach a configuration reader. + * + * $config->detach($reader); + * + * @param Kohana_Config_Source $source instance + * @return $this + */ + public function detach(Kohana_Config_Source $source) + { + if (($key = array_search($source, $this->_sources)) !== FALSE) + { + // Remove the writer + unset($this->_sources[$key]); + } + + return $this; + } + + /** + * Load a configuration group. Searches all the config sources, merging all the + * directives found into a single config group. Any changes made to the config + * in this group will be mirrored across all writable sources. + * + * $array = $config->load($name); + * + * See [Kohana_Config_Group] for more info + * + * @param string $group configuration group name + * @return Kohana_Config_Group + * @throws Kohana_Exception + */ + public function load($group) + { + if ( ! count($this->_sources)) + { + throw new Kohana_Exception('No configuration sources attached'); + } + + if (empty($group)) + { + throw new Kohana_Exception("Need to specify a config group"); + } + + if ( ! is_string($group)) + { + throw new Kohana_Exception("Config group must be a string"); + } + + if (strpos($group, '.') !== FALSE) + { + // Split the config group and path + list($group, $path) = explode('.', $group, 2); + } + + if (isset($this->_groups[$group])) + { + if (isset($path)) + { + return Arr::path($this->_groups[$group], $path, NULL, '.'); + } + return $this->_groups[$group]; + } + + $config = array(); + + // We search from the "lowest" source and work our way up + $sources = array_reverse($this->_sources); + + foreach ($sources as $source) + { + if ($source instanceof Kohana_Config_Reader) + { + if ($source_config = $source->load($group)) + { + $config = Arr::merge($config, $source_config); + } + } + } + + $this->_groups[$group] = new Config_Group($this, $group, $config); + + if (isset($path)) + { + return Arr::path($config, $path, NULL, '.'); + } + + return $this->_groups[$group]; + } + + /** + * Copy one configuration group to all of the other writers. + * + * $config->copy($name); + * + * @param string $group configuration group name + * @return $this + */ + public function copy($group) + { + // Load the configuration group + $config = $this->load($group); + + foreach ($config->as_array() as $key => $value) + { + $this->_write_config($group, $key, $value); + } + + return $this; + } + + /** + * Callback used by the config group to store changes made to configuration + * + * @param string $group Group name + * @param string $key Variable name + * @param mixed $value The new value + * @return Kohana_Config Chainable instance + */ + public function _write_config($group, $key, $value) + { + foreach ($this->_sources as $source) + { + if ( ! ($source instanceof Kohana_Config_Writer)) + { + continue; + } + + // Copy each value in the config + $source->write($group, $key, $value); + } + + return $this; + } + +} // End Kohana_Config diff --git a/system/classes/Kohana/Config/File.php b/system/classes/Kohana/Config/File.php new file mode 100644 index 0000000..8d514f6 --- /dev/null +++ b/system/classes/Kohana/Config/File.php @@ -0,0 +1,15 @@ +_directory = trim($directory, '/'); + } + + /** + * Load and merge all of the configuration files in this group. + * + * $config->load($name); + * + * @param string $group configuration group name + * @return $this current object + * @uses Kohana::load + */ + public function load($group) + { + $config = array(); + + if ($files = Kohana::find_file($this->_directory, $group, NULL, TRUE)) + { + foreach ($files as $file) + { + // Merge each file to the configuration array + $config = Arr::merge($config, Kohana::load($file)); + } + } + + return $config; + } + +} // End Kohana_Config diff --git a/system/classes/Kohana/Config/Group.php b/system/classes/Kohana/Config/Group.php new file mode 100644 index 0000000..ad32cf8 --- /dev/null +++ b/system/classes/Kohana/Config/Group.php @@ -0,0 +1,130 @@ +_parent_instance = $instance; + $this->_group_name = $group; + + parent::__construct($config, ArrayObject::ARRAY_AS_PROPS); + } + + /** + * Return the current group in serialized form. + * + * echo $config; + * + * @return string + */ + public function __toString() + { + return serialize($this->getArrayCopy()); + } + + /** + * Alias for getArrayCopy() + * + * @return array Array copy of the group's config + */ + public function as_array() + { + return $this->getArrayCopy(); + } + + /** + * Returns the config group's name + * + * @return string The group name + */ + public function group_name() + { + return $this->_group_name; + } + + /** + * Get a variable from the configuration or return the default value. + * + * $value = $config->get($key); + * + * @param string $key array key + * @param mixed $default default value + * @return mixed + */ + public function get($key, $default = NULL) + { + return $this->offsetExists($key) ? $this->offsetGet($key) : $default; + } + + /** + * Sets a value in the configuration array. + * + * $config->set($key, $new_value); + * + * @param string $key array key + * @param mixed $value array value + * @return $this + */ + public function set($key, $value) + { + $this->offsetSet($key, $value); + + return $this; + } + + /** + * Overrides ArrayObject::offsetSet() + * This method is called when config is changed via + * + * $config->var = 'asd'; + * + * // OR + * + * $config['var'] = 'asd'; + * + * @param string $key The key of the config item we're changing + * @param mixed $value The new array value + */ + public function offsetSet($key, $value) + { + $this->_parent_instance->_write_config($this->_group_name, $key, $value); + + return parent::offsetSet($key, $value); + } +} diff --git a/system/classes/Kohana/Config/Reader.php b/system/classes/Kohana/Config/Reader.php new file mode 100644 index 0000000..6b08a3b --- /dev/null +++ b/system/classes/Kohana/Config/Reader.php @@ -0,0 +1,25 @@ +before(); + * $controller->action_bar(); + * $controller->after(); + * + * The controller action should add the output it creates to + * `$this->response->body($output)`, typically in the form of a [View], during the + * "action" part of execution. + * + * @package Kohana + * @category Controller + * @author Kohana Team + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +abstract class Kohana_Controller { + + /** + * @var Request Request that created the controller + */ + public $request; + + /** + * @var Response The response that will be returned from controller + */ + public $response; + + /** + * Creates a new controller instance. Each controller must be constructed + * with the request object that created it. + * + * @param Request $request Request that created the controller + * @param Response $response The request's response + * @return void + */ + public function __construct(Request $request, Response $response) + { + // Assign the request to the controller + $this->request = $request; + + // Assign a response to the controller + $this->response = $response; + } + + /** + * Executes the given action and calls the [Controller::before] and [Controller::after] methods. + * + * Can also be used to catch exceptions from actions in a single place. + * + * 1. Before the controller action is called, the [Controller::before] method + * will be called. + * 2. Next the controller action will be called. + * 3. After the controller action is called, the [Controller::after] method + * will be called. + * + * @throws HTTP_Exception_404 + * @return Response + */ + public function execute() + { + // Execute the "before action" method + $this->before(); + + // Determine the action to use + $action = 'action_'.$this->request->action(); + + // If the action doesn't exist, it's a 404 + if ( ! method_exists($this, $action)) + { + throw HTTP_Exception::factory(404, + 'The requested URL :uri was not found on this server.', + array(':uri' => $this->request->uri()) + )->request($this->request); + } + + // Execute the action itself + $this->{$action}(); + + // Execute the "after action" method + $this->after(); + + // Return the response + return $this->response; + } + + /** + * Automatically executed before the controller action. Can be used to set + * class properties, do authorization checks, and execute other custom code. + * + * @return void + */ + public function before() + { + // Nothing by default + } + + /** + * Automatically executed after the controller action. Can be used to apply + * transformation to the response, add extra output, and execute + * other custom code. + * + * @return void + */ + public function after() + { + // Nothing by default + } + + /** + * Issues a HTTP redirect. + * + * Proxies to the [HTTP::redirect] method. + * + * @param string $uri URI to redirect to + * @param int $code HTTP Status code to use for the redirect + * @throws HTTP_Exception + */ + public static function redirect($uri = '', $code = 302) + { + return HTTP::redirect($uri, $code); + } + + /** + * Checks the browser cache to see the response needs to be returned, + * execution will halt and a 304 Not Modified will be sent if the + * browser cache is up to date. + * + * $this->check_cache(sha1($content)); + * + * @param string $etag Resource Etag + * @return Response + */ + protected function check_cache($etag = NULL) + { + return HTTP::check_cache($this->request, $this->response, $etag); + } + +} // End Controller \ No newline at end of file diff --git a/system/classes/Kohana/Controller/Template.php b/system/classes/Kohana/Controller/Template.php new file mode 100644 index 0000000..e091757 --- /dev/null +++ b/system/classes/Kohana/Controller/Template.php @@ -0,0 +1,50 @@ +auto_render === TRUE) + { + // Load the template + $this->template = View::factory($this->template); + } + } + + /** + * Assigns the template [View] as the request response. + */ + public function after() + { + if ($this->auto_render === TRUE) + { + $this->response->body($this->template->render()); + } + + parent::after(); + } + +} // End Controller_Template diff --git a/system/classes/Kohana/Cookie.php b/system/classes/Kohana/Cookie.php new file mode 100644 index 0000000..239d8c8 --- /dev/null +++ b/system/classes/Kohana/Cookie.php @@ -0,0 +1,161 @@ +
    Recommended setting: `TRUE` while developing, `FALSE` on production servers. | `TRUE` + * `boolean` | profile | Whether to enable the [Profiler](kohana/profiling).

    Recommended setting: `TRUE` while developing, `FALSE` on production servers. | `TRUE` + * `boolean` | caching | Cache file locations to speed up [Kohana::find_file]. This has nothing to do with [Kohana::cache], [Fragments](kohana/fragments) or the [Cache module](cache).

    Recommended setting: `FALSE` while developing, `TRUE` on production servers. | `FALSE` + * `boolean` | expose | Set the X-Powered-By header + * + * @throws Kohana_Exception + * @param array $settings Array of settings. See above. + * @return void + * @uses Kohana::globals + * @uses Kohana::sanitize + * @uses Kohana::cache + * @uses Profiler + */ + public static function init(array $settings = NULL) + { + if (Kohana::$_init) + { + // Do not allow execution twice + return; + } + + // Kohana is now initialized + Kohana::$_init = TRUE; + + if (isset($settings['profile'])) + { + // Enable profiling + Kohana::$profiling = (bool) $settings['profile']; + } + + // Start an output buffer + ob_start(); + + if (isset($settings['errors'])) + { + // Enable error handling + Kohana::$errors = (bool) $settings['errors']; + } + + if (Kohana::$errors === TRUE) + { + // Enable Kohana exception handling, adds stack traces and error source. + set_exception_handler(array('Kohana_Exception', 'handler')); + + // Enable Kohana error handling, converts all PHP errors to exceptions. + set_error_handler(array('Kohana', 'error_handler')); + } + + /** + * Enable xdebug parameter collection in development mode to improve fatal stack traces. + */ + if (Kohana::$environment == Kohana::DEVELOPMENT AND extension_loaded('xdebug')) + { + ini_set('xdebug.collect_params', 3); + } + + // Enable the Kohana shutdown handler, which catches E_FATAL errors. + register_shutdown_function(array('Kohana', 'shutdown_handler')); + + if (ini_get('register_globals')) + { + // Reverse the effects of register_globals + Kohana::globals(); + } + + if (isset($settings['expose'])) + { + Kohana::$expose = (bool) $settings['expose']; + } + + // Determine if we are running in a Windows environment + Kohana::$is_windows = (DIRECTORY_SEPARATOR === '\\'); + + // Determine if we are running in safe mode + Kohana::$safe_mode = (bool) ini_get('safe_mode'); + + if (isset($settings['cache_dir'])) + { + if ( ! is_dir($settings['cache_dir'])) + { + try + { + // Create the cache directory + mkdir($settings['cache_dir'], 0755, TRUE); + + // Set permissions (must be manually set to fix umask issues) + chmod($settings['cache_dir'], 0755); + } + catch (Exception $e) + { + throw new Kohana_Exception('Could not create cache directory :dir', + array(':dir' => Debug::path($settings['cache_dir']))); + } + } + + // Set the cache directory path + Kohana::$cache_dir = realpath($settings['cache_dir']); + } + else + { + // Use the default cache directory + Kohana::$cache_dir = APPPATH.'cache'; + } + + if ( ! is_writable(Kohana::$cache_dir)) + { + throw new Kohana_Exception('Directory :dir must be writable', + array(':dir' => Debug::path(Kohana::$cache_dir))); + } + + if (isset($settings['cache_life'])) + { + // Set the default cache lifetime + Kohana::$cache_life = (int) $settings['cache_life']; + } + + if (isset($settings['caching'])) + { + // Enable or disable internal caching + Kohana::$caching = (bool) $settings['caching']; + } + + if (Kohana::$caching === TRUE) + { + // Load the file path cache + Kohana::$_files = Kohana::cache('Kohana::find_file()'); + } + + if (isset($settings['charset'])) + { + // Set the system character set + Kohana::$charset = strtolower($settings['charset']); + } + + if (function_exists('mb_internal_encoding')) + { + // Set the MB extension encoding to the same character set + mb_internal_encoding(Kohana::$charset); + } + + if (isset($settings['base_url'])) + { + // Set the base URL + Kohana::$base_url = rtrim($settings['base_url'], '/').'/'; + } + + if (isset($settings['index_file'])) + { + // Set the index file + Kohana::$index_file = trim($settings['index_file'], '/'); + } + + // Determine if the extremely evil magic quotes are enabled + Kohana::$magic_quotes = (version_compare(PHP_VERSION, '5.4') < 0 AND get_magic_quotes_gpc()); + + // Sanitize all request variables + $_GET = Kohana::sanitize($_GET); + $_POST = Kohana::sanitize($_POST); + $_COOKIE = Kohana::sanitize($_COOKIE); + + // Load the logger if one doesn't already exist + if ( ! Kohana::$log instanceof Log) + { + Kohana::$log = Log::instance(); + } + + // Load the config if one doesn't already exist + if ( ! Kohana::$config instanceof Config) + { + Kohana::$config = new Config; + } + } + + /** + * Cleans up the environment: + * + * - Restore the previous error and exception handlers + * - Destroy the Kohana::$log and Kohana::$config objects + * + * @return void + */ + public static function deinit() + { + if (Kohana::$_init) + { + // Removed the autoloader + spl_autoload_unregister(array('Kohana', 'auto_load')); + + if (Kohana::$errors) + { + // Go back to the previous error handler + restore_error_handler(); + + // Go back to the previous exception handler + restore_exception_handler(); + } + + // Destroy objects created by init + Kohana::$log = Kohana::$config = NULL; + + // Reset internal storage + Kohana::$_modules = Kohana::$_files = array(); + Kohana::$_paths = array(APPPATH, SYSPATH); + + // Reset file cache status + Kohana::$_files_changed = FALSE; + + // Kohana is no longer initialized + Kohana::$_init = FALSE; + } + } + + /** + * Reverts the effects of the `register_globals` PHP setting by unsetting + * all global varibles except for the default super globals (GPCS, etc), + * which is a [potential security hole.][ref-wikibooks] + * + * This is called automatically by [Kohana::init] if `register_globals` is + * on. + * + * + * [ref-wikibooks]: http://en.wikibooks.org/wiki/PHP_Programming/Register_Globals + * + * @return void + */ + public static function globals() + { + if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS'])) + { + // Prevent malicious GLOBALS overload attack + echo "Global variable overload attack detected! Request aborted.\n"; + + // Exit with an error status + exit(1); + } + + // Get the variable names of all globals + $global_variables = array_keys($GLOBALS); + + // Remove the standard global variables from the list + $global_variables = array_diff($global_variables, array( + '_COOKIE', + '_ENV', + '_GET', + '_FILES', + '_POST', + '_REQUEST', + '_SERVER', + '_SESSION', + 'GLOBALS', + )); + + foreach ($global_variables as $name) + { + // Unset the global variable, effectively disabling register_globals + unset($GLOBALS[$name]); + } + } + + /** + * Recursively sanitizes an input variable: + * + * - Strips slashes if magic quotes are enabled + * - Normalizes all newlines to LF + * + * @param mixed $value any variable + * @return mixed sanitized variable + */ + public static function sanitize($value) + { + if (is_array($value) OR is_object($value)) + { + foreach ($value as $key => $val) + { + // Recursively clean each value + $value[$key] = Kohana::sanitize($val); + } + } + elseif (is_string($value)) + { + if (Kohana::$magic_quotes === TRUE) + { + // Remove slashes added by magic quotes + $value = stripslashes($value); + } + + if (strpos($value, "\r") !== FALSE) + { + // Standardize newlines + $value = str_replace(array("\r\n", "\r"), "\n", $value); + } + } + + return $value; + } + + /** + * Provides auto-loading support of classes that follow Kohana's [class + * naming conventions](kohana/conventions#class-names-and-file-location). + * See [Loading Classes](kohana/autoloading) for more information. + * + * // Loads classes/My/Class/Name.php + * Kohana::auto_load('My_Class_Name'); + * + * or with a custom directory: + * + * // Loads vendor/My/Class/Name.php + * Kohana::auto_load('My_Class_Name', 'vendor'); + * + * You should never have to call this function, as simply calling a class + * will cause it to be called. + * + * This function must be enabled as an autoloader in the bootstrap: + * + * spl_autoload_register(array('Kohana', 'auto_load')); + * + * @param string $class Class name + * @param string $directory Directory to load from + * @return boolean + */ + public static function auto_load($class, $directory = 'classes') + { + // Transform the class name according to PSR-0 + $class = ltrim($class, '\\'); + $file = ''; + $namespace = ''; + + if ($last_namespace_position = strripos($class, '\\')) + { + $namespace = substr($class, 0, $last_namespace_position); + $class = substr($class, $last_namespace_position + 1); + $file = str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR; + } + + $file .= str_replace('_', DIRECTORY_SEPARATOR, $class); + + if ($path = Kohana::find_file($directory, $file)) + { + // Load the class file + require $path; + + // Class has been found + return TRUE; + } + + // Class is not in the filesystem + return FALSE; + } + + /** + * Provides auto-loading support of classes that follow Kohana's old class + * naming conventions. + * + * This is included for compatibility purposes with older modules. + * + * @param string $class Class name + * @param string $directory Directory to load from + * @return boolean + */ + public static function auto_load_lowercase($class, $directory = 'classes') + { + // Transform the class name into a path + $file = str_replace('_', DIRECTORY_SEPARATOR, strtolower($class)); + + if ($path = Kohana::find_file($directory, $file)) + { + // Load the class file + require $path; + + // Class has been found + return TRUE; + } + + // Class is not in the filesystem + return FALSE; + } + + /** + * Changes the currently enabled modules. Module paths may be relative + * or absolute, but must point to a directory: + * + * Kohana::modules(array('modules/foo', MODPATH.'bar')); + * + * @param array $modules list of module paths + * @return array enabled modules + */ + public static function modules(array $modules = NULL) + { + if ($modules === NULL) + { + // Not changing modules, just return the current set + return Kohana::$_modules; + } + + // Start a new list of include paths, APPPATH first + $paths = array(APPPATH); + + foreach ($modules as $name => $path) + { + if (is_dir($path)) + { + // Add the module to include paths + $paths[] = $modules[$name] = realpath($path).DIRECTORY_SEPARATOR; + } + else + { + // This module is invalid, remove it + throw new Kohana_Exception('Attempted to load an invalid or missing module \':module\' at \':path\'', array( + ':module' => $name, + ':path' => Debug::path($path), + )); + } + } + + // Finish the include paths by adding SYSPATH + $paths[] = SYSPATH; + + // Set the new include paths + Kohana::$_paths = $paths; + + // Set the current module list + Kohana::$_modules = $modules; + + foreach (Kohana::$_modules as $path) + { + $init = $path.'init'.EXT; + + if (is_file($init)) + { + // Include the module initialization file once + require_once $init; + } + } + + return Kohana::$_modules; + } + + /** + * Returns the the currently active include paths, including the + * application, system, and each module's path. + * + * @return array + */ + public static function include_paths() + { + return Kohana::$_paths; + } + + /** + * Searches for a file in the [Cascading Filesystem](kohana/files), and + * returns the path to the file that has the highest precedence, so that it + * can be included. + * + * When searching the "config", "messages", or "i18n" directories, or when + * the `$array` flag is set to true, an array of all the files that match + * that path in the [Cascading Filesystem](kohana/files) will be returned. + * These files will return arrays which must be merged together. + * + * If no extension is given, the default extension (`EXT` set in + * `index.php`) will be used. + * + * // Returns an absolute path to views/template.php + * Kohana::find_file('views', 'template'); + * + * // Returns an absolute path to media/css/style.css + * Kohana::find_file('media', 'css/style', 'css'); + * + * // Returns an array of all the "mimes" configuration files + * Kohana::find_file('config', 'mimes'); + * + * @param string $dir directory name (views, i18n, classes, extensions, etc.) + * @param string $file filename with subdirectory + * @param string $ext extension to search for + * @param boolean $array return an array of files? + * @return array a list of files when $array is TRUE + * @return string single file path + */ + public static function find_file($dir, $file, $ext = NULL, $array = FALSE) + { + if ($ext === NULL) + { + // Use the default extension + $ext = EXT; + } + elseif ($ext) + { + // Prefix the extension with a period + $ext = ".{$ext}"; + } + else + { + // Use no extension + $ext = ''; + } + + // Create a partial path of the filename + $path = $dir.DIRECTORY_SEPARATOR.$file.$ext; + + if (Kohana::$caching === TRUE AND isset(Kohana::$_files[$path.($array ? '_array' : '_path')])) + { + // This path has been cached + return Kohana::$_files[$path.($array ? '_array' : '_path')]; + } + + if (Kohana::$profiling === TRUE AND class_exists('Profiler', FALSE)) + { + // Start a new benchmark + $benchmark = Profiler::start('Kohana', __FUNCTION__); + } + + if ($array OR $dir === 'config' OR $dir === 'i18n' OR $dir === 'messages') + { + // Include paths must be searched in reverse + $paths = array_reverse(Kohana::$_paths); + + // Array of files that have been found + $found = array(); + + foreach ($paths as $dir) + { + if (is_file($dir.$path)) + { + // This path has a file, add it to the list + $found[] = $dir.$path; + } + } + } + else + { + // The file has not been found yet + $found = FALSE; + + foreach (Kohana::$_paths as $dir) + { + if (is_file($dir.$path)) + { + // A path has been found + $found = $dir.$path; + + // Stop searching + break; + } + } + } + + if (Kohana::$caching === TRUE) + { + // Add the path to the cache + Kohana::$_files[$path.($array ? '_array' : '_path')] = $found; + + // Files have been changed + Kohana::$_files_changed = TRUE; + } + + if (isset($benchmark)) + { + // Stop the benchmark + Profiler::stop($benchmark); + } + + return $found; + } + + /** + * Recursively finds all of the files in the specified directory at any + * location in the [Cascading Filesystem](kohana/files), and returns an + * array of all the files found, sorted alphabetically. + * + * // Find all view files. + * $views = Kohana::list_files('views'); + * + * @param string $directory directory name + * @param array $paths list of paths to search + * @return array + */ + public static function list_files($directory = NULL, array $paths = NULL) + { + if ($directory !== NULL) + { + // Add the directory separator + $directory .= DIRECTORY_SEPARATOR; + } + + if ($paths === NULL) + { + // Use the default paths + $paths = Kohana::$_paths; + } + + // Create an array for the files + $found = array(); + + foreach ($paths as $path) + { + if (is_dir($path.$directory)) + { + // Create a new directory iterator + $dir = new DirectoryIterator($path.$directory); + + foreach ($dir as $file) + { + // Get the file name + $filename = $file->getFilename(); + + if ($filename[0] === '.' OR $filename[strlen($filename)-1] === '~') + { + // Skip all hidden files and UNIX backup files + continue; + } + + // Relative filename is the array key + $key = $directory.$filename; + + if ($file->isDir()) + { + if ($sub_dir = Kohana::list_files($key, $paths)) + { + if (isset($found[$key])) + { + // Append the sub-directory list + $found[$key] += $sub_dir; + } + else + { + // Create a new sub-directory list + $found[$key] = $sub_dir; + } + } + } + else + { + if ( ! isset($found[$key])) + { + // Add new files to the list + $found[$key] = realpath($file->getPathName()); + } + } + } + } + } + + // Sort the results alphabetically + ksort($found); + + return $found; + } + + /** + * Loads a file within a totally empty scope and returns the output: + * + * $foo = Kohana::load('foo.php'); + * + * @param string $file + * @return mixed + */ + public static function load($file) + { + return include $file; + } + + /** + * Provides simple file-based caching for strings and arrays: + * + * // Set the "foo" cache + * Kohana::cache('foo', 'hello, world'); + * + * // Get the "foo" cache + * $foo = Kohana::cache('foo'); + * + * All caches are stored as PHP code, generated with [var_export][ref-var]. + * Caching objects may not work as expected. Storing references or an + * object or array that has recursion will cause an E_FATAL. + * + * The cache directory and default cache lifetime is set by [Kohana::init] + * + * [ref-var]: http://php.net/var_export + * + * @throws Kohana_Exception + * @param string $name name of the cache + * @param mixed $data data to cache + * @param integer $lifetime number of seconds the cache is valid for + * @return mixed for getting + * @return boolean for setting + */ + public static function cache($name, $data = NULL, $lifetime = NULL) + { + // Cache file is a hash of the name + $file = sha1($name).'.txt'; + + // Cache directories are split by keys to prevent filesystem overload + $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR.$file[0].$file[1].DIRECTORY_SEPARATOR; + + if ($lifetime === NULL) + { + // Use the default lifetime + $lifetime = Kohana::$cache_life; + } + + if ($data === NULL) + { + if (is_file($dir.$file)) + { + if ((time() - filemtime($dir.$file)) < $lifetime) + { + // Return the cache + try + { + return unserialize(file_get_contents($dir.$file)); + } + catch (Exception $e) + { + // Cache is corrupt, let return happen normally. + } + } + else + { + try + { + // Cache has expired + unlink($dir.$file); + } + catch (Exception $e) + { + // Cache has mostly likely already been deleted, + // let return happen normally. + } + } + } + + // Cache not found + return NULL; + } + + if ( ! is_dir($dir)) + { + // Create the cache directory + mkdir($dir, 0777, TRUE); + + // Set permissions (must be manually set to fix umask issues) + chmod($dir, 0777); + } + + // Force the data to be a string + $data = serialize($data); + + try + { + // Write the cache + return (bool) file_put_contents($dir.$file, $data, LOCK_EX); + } + catch (Exception $e) + { + // Failed to write cache + return FALSE; + } + } + + /** + * Get a message from a file. Messages are arbitary strings that are stored + * in the `messages/` directory and reference by a key. Translation is not + * performed on the returned values. See [message files](kohana/files/messages) + * for more information. + * + * // Get "username" from messages/text.php + * $username = Kohana::message('text', 'username'); + * + * @param string $file file name + * @param string $path key path to get + * @param mixed $default default value if the path does not exist + * @return string message string for the given path + * @return array complete message list, when no path is specified + * @uses Arr::merge + * @uses Arr::path + */ + public static function message($file, $path = NULL, $default = NULL) + { + static $messages; + + if ( ! isset($messages[$file])) + { + // Create a new message list + $messages[$file] = array(); + + if ($files = Kohana::find_file('messages', $file)) + { + foreach ($files as $f) + { + // Combine all the messages recursively + $messages[$file] = Arr::merge($messages[$file], Kohana::load($f)); + } + } + } + + if ($path === NULL) + { + // Return all of the messages + return $messages[$file]; + } + else + { + // Get a message using the path + return Arr::path($messages[$file], $path, $default); + } + } + + /** + * PHP error handler, converts all errors into ErrorExceptions. This handler + * respects error_reporting settings. + * + * @throws ErrorException + * @return TRUE + */ + public static function error_handler($code, $error, $file = NULL, $line = NULL) + { + if (error_reporting() & $code) + { + // This error is not suppressed by current error reporting settings + // Convert the error into an ErrorException + throw new ErrorException($error, $code, 0, $file, $line); + } + + // Do not execute the PHP error handler + return TRUE; + } + + /** + * Catches errors that are not caught by the error handler, such as E_PARSE. + * + * @uses Kohana_Exception::handler + * @return void + */ + public static function shutdown_handler() + { + if ( ! Kohana::$_init) + { + // Do not execute when not active + return; + } + + try + { + if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE) + { + // Write the file path cache + Kohana::cache('Kohana::find_file()', Kohana::$_files); + } + } + catch (Exception $e) + { + // Pass the exception to the handler + Kohana_Exception::handler($e); + } + + if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'], Kohana::$shutdown_errors)) + { + // Clean the output buffer + ob_get_level() AND ob_clean(); + + // Fake an exception for nice debugging + Kohana_Exception::handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); + + // Shutdown now to avoid a "death loop" + exit(1); + } + } + + /** + * Generates a version string based on the variables defined above. + * + * @return string + */ + public static function version() + { + return 'Kohana Framework '.Kohana::VERSION.' ('.Kohana::CODENAME.')'; + } + +} // End Kohana diff --git a/system/classes/Kohana/Date.php b/system/classes/Kohana/Date.php new file mode 100644 index 0000000..2c2ce39 --- /dev/null +++ b/system/classes/Kohana/Date.php @@ -0,0 +1,603 @@ +. + * + * @param string $remote timezone that to find the offset of + * @param string $local timezone used as the baseline + * @param mixed $now UNIX timestamp or date string + * @return integer + */ + public static function offset($remote, $local = NULL, $now = NULL) + { + if ($local === NULL) + { + // Use the default timezone + $local = date_default_timezone_get(); + } + + if (is_int($now)) + { + // Convert the timestamp into a string + $now = date(DateTime::RFC2822, $now); + } + + // Create timezone objects + $zone_remote = new DateTimeZone($remote); + $zone_local = new DateTimeZone($local); + + // Create date objects from timezones + $time_remote = new DateTime($now, $zone_remote); + $time_local = new DateTime($now, $zone_local); + + // Find the offset + $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local); + + return $offset; + } + + /** + * Number of seconds in a minute, incrementing by a step. Typically used as + * a shortcut for generating a list that can used in a form. + * + * $seconds = Date::seconds(); // 01, 02, 03, ..., 58, 59, 60 + * + * @param integer $step amount to increment each step by, 1 to 30 + * @param integer $start start value + * @param integer $end end value + * @return array A mirrored (foo => foo) array from 1-60. + */ + public static function seconds($step = 1, $start = 0, $end = 60) + { + // Always integer + $step = (int) $step; + + $seconds = array(); + + for ($i = $start; $i < $end; $i += $step) + { + $seconds[$i] = sprintf('%02d', $i); + } + + return $seconds; + } + + /** + * Number of minutes in an hour, incrementing by a step. Typically used as + * a shortcut for generating a list that can be used in a form. + * + * $minutes = Date::minutes(); // 05, 10, 15, ..., 50, 55, 60 + * + * @uses Date::seconds + * @param integer $step amount to increment each step by, 1 to 30 + * @return array A mirrored (foo => foo) array from 1-60. + */ + public static function minutes($step = 5) + { + // Because there are the same number of minutes as seconds in this set, + // we choose to re-use seconds(), rather than creating an entirely new + // function. Shhhh, it's cheating! ;) There are several more of these + // in the following methods. + return Date::seconds($step); + } + + /** + * Number of hours in a day. Typically used as a shortcut for generating a + * list that can be used in a form. + * + * $hours = Date::hours(); // 01, 02, 03, ..., 10, 11, 12 + * + * @param integer $step amount to increment each step by + * @param boolean $long use 24-hour time + * @param integer $start the hour to start at + * @return array A mirrored (foo => foo) array from start-12 or start-23. + */ + public static function hours($step = 1, $long = FALSE, $start = NULL) + { + // Default values + $step = (int) $step; + $long = (bool) $long; + $hours = array(); + + // Set the default start if none was specified. + if ($start === NULL) + { + $start = ($long === FALSE) ? 1 : 0; + } + + $hours = array(); + + // 24-hour time has 24 hours, instead of 12 + $size = ($long === TRUE) ? 23 : 12; + + for ($i = $start; $i <= $size; $i += $step) + { + $hours[$i] = (string) $i; + } + + return $hours; + } + + /** + * Returns AM or PM, based on a given hour (in 24 hour format). + * + * $type = Date::ampm(12); // PM + * $type = Date::ampm(1); // AM + * + * @param integer $hour number of the hour + * @return string + */ + public static function ampm($hour) + { + // Always integer + $hour = (int) $hour; + + return ($hour > 11) ? 'PM' : 'AM'; + } + + /** + * Adjusts a non-24-hour number into a 24-hour number. + * + * $hour = Date::adjust(3, 'pm'); // 15 + * + * @param integer $hour hour to adjust + * @param string $ampm AM or PM + * @return string + */ + public static function adjust($hour, $ampm) + { + $hour = (int) $hour; + $ampm = strtolower($ampm); + + switch ($ampm) + { + case 'am': + if ($hour == 12) + { + $hour = 0; + } + break; + case 'pm': + if ($hour < 12) + { + $hour += 12; + } + break; + } + + return sprintf('%02d', $hour); + } + + /** + * Number of days in a given month and year. Typically used as a shortcut + * for generating a list that can be used in a form. + * + * Date::days(4, 2010); // 1, 2, 3, ..., 28, 29, 30 + * + * @param integer $month number of month + * @param integer $year number of year to check month, defaults to the current year + * @return array A mirrored (foo => foo) array of the days. + */ + public static function days($month, $year = FALSE) + { + static $months; + + if ($year === FALSE) + { + // Use the current year by default + $year = date('Y'); + } + + // Always integers + $month = (int) $month; + $year = (int) $year; + + // We use caching for months, because time functions are used + if (empty($months[$year][$month])) + { + $months[$year][$month] = array(); + + // Use date to find the number of days in the given month + $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1; + + for ($i = 1; $i < $total; $i++) + { + $months[$year][$month][$i] = (string) $i; + } + } + + return $months[$year][$month]; + } + + /** + * Number of months in a year. Typically used as a shortcut for generating + * a list that can be used in a form. + * + * By default a mirrored array of $month_number => $month_number is returned + * + * Date::months(); + * // aray(1 => 1, 2 => 2, 3 => 3, ..., 12 => 12) + * + * But you can customise this by passing in either Date::MONTHS_LONG + * + * Date::months(Date::MONTHS_LONG); + * // array(1 => 'January', 2 => 'February', ..., 12 => 'December') + * + * Or Date::MONTHS_SHORT + * + * Date::months(Date::MONTHS_SHORT); + * // array(1 => 'Jan', 2 => 'Feb', ..., 12 => 'Dec') + * + * @uses Date::hours + * @param string $format The format to use for months + * @return array An array of months based on the specified format + */ + public static function months($format = NULL) + { + $months = array(); + + if ($format === Date::MONTHS_LONG OR $format === Date::MONTHS_SHORT) + { + for ($i = 1; $i <= 12; ++$i) + { + $months[$i] = strftime($format, mktime(0, 0, 0, $i, 1)); + } + } + else + { + $months = Date::hours(); + } + + return $months; + } + + /** + * Returns an array of years between a starting and ending year. By default, + * the the current year - 5 and current year + 5 will be used. Typically used + * as a shortcut for generating a list that can be used in a form. + * + * $years = Date::years(2000, 2010); // 2000, 2001, ..., 2009, 2010 + * + * @param integer $start starting year (default is current year - 5) + * @param integer $end ending year (default is current year + 5) + * @return array + */ + public static function years($start = FALSE, $end = FALSE) + { + // Default values + $start = ($start === FALSE) ? (date('Y') - 5) : (int) $start; + $end = ($end === FALSE) ? (date('Y') + 5) : (int) $end; + + $years = array(); + + for ($i = $start; $i <= $end; $i++) + { + $years[$i] = (string) $i; + } + + return $years; + } + + /** + * Returns time difference between two timestamps, in human readable format. + * If the second timestamp is not given, the current time will be used. + * Also consider using [Date::fuzzy_span] when displaying a span. + * + * $span = Date::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2) + * $span = Date::span(60, 182, 'minutes'); // 2 + * + * @param integer $remote timestamp to find the span of + * @param integer $local timestamp to use as the baseline + * @param string $output formatting string + * @return string when only a single output is requested + * @return array associative list of all outputs requested + */ + public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') + { + // Normalize output + $output = trim(strtolower( (string) $output)); + + if ( ! $output) + { + // Invalid output + return FALSE; + } + + // Array with the output formats + $output = preg_split('/[^a-z]+/', $output); + + // Convert the list of outputs to an associative array + $output = array_combine($output, array_fill(0, count($output), 0)); + + // Make the output values into keys + extract(array_flip($output), EXTR_SKIP); + + if ($local === NULL) + { + // Calculate the span from the current time + $local = time(); + } + + // Calculate timespan (seconds) + $timespan = abs($remote - $local); + + if (isset($output['years'])) + { + $timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR)); + } + + if (isset($output['months'])) + { + $timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH)); + } + + if (isset($output['weeks'])) + { + $timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK)); + } + + if (isset($output['days'])) + { + $timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY)); + } + + if (isset($output['hours'])) + { + $timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR)); + } + + if (isset($output['minutes'])) + { + $timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE)); + } + + // Seconds ago, 1 + if (isset($output['seconds'])) + { + $output['seconds'] = $timespan; + } + + if (count($output) === 1) + { + // Only a single output was requested, return it + return array_pop($output); + } + + // Return array + return $output; + } + + /** + * Returns the difference between a time and now in a "fuzzy" way. + * Displaying a fuzzy time instead of a date is usually faster to read and understand. + * + * $span = Date::fuzzy_span(time() - 10); // "moments ago" + * $span = Date::fuzzy_span(time() + 20); // "in moments" + * + * A second parameter is available to manually set the "local" timestamp, + * however this parameter shouldn't be needed in normal usage and is only + * included for unit tests + * + * @param integer $timestamp "remote" timestamp + * @param integer $local_timestamp "local" timestamp, defaults to time() + * @return string + */ + public static function fuzzy_span($timestamp, $local_timestamp = NULL) + { + $local_timestamp = ($local_timestamp === NULL) ? time() : (int) $local_timestamp; + + // Determine the difference in seconds + $offset = abs($local_timestamp - $timestamp); + + if ($offset <= Date::MINUTE) + { + $span = 'moments'; + } + elseif ($offset < (Date::MINUTE * 20)) + { + $span = 'a few minutes'; + } + elseif ($offset < Date::HOUR) + { + $span = 'less than an hour'; + } + elseif ($offset < (Date::HOUR * 4)) + { + $span = 'a couple of hours'; + } + elseif ($offset < Date::DAY) + { + $span = 'less than a day'; + } + elseif ($offset < (Date::DAY * 2)) + { + $span = 'about a day'; + } + elseif ($offset < (Date::DAY * 4)) + { + $span = 'a couple of days'; + } + elseif ($offset < Date::WEEK) + { + $span = 'less than a week'; + } + elseif ($offset < (Date::WEEK * 2)) + { + $span = 'about a week'; + } + elseif ($offset < Date::MONTH) + { + $span = 'less than a month'; + } + elseif ($offset < (Date::MONTH * 2)) + { + $span = 'about a month'; + } + elseif ($offset < (Date::MONTH * 4)) + { + $span = 'a couple of months'; + } + elseif ($offset < Date::YEAR) + { + $span = 'less than a year'; + } + elseif ($offset < (Date::YEAR * 2)) + { + $span = 'about a year'; + } + elseif ($offset < (Date::YEAR * 4)) + { + $span = 'a couple of years'; + } + elseif ($offset < (Date::YEAR * 8)) + { + $span = 'a few years'; + } + elseif ($offset < (Date::YEAR * 12)) + { + $span = 'about a decade'; + } + elseif ($offset < (Date::YEAR * 24)) + { + $span = 'a couple of decades'; + } + elseif ($offset < (Date::YEAR * 64)) + { + $span = 'several decades'; + } + else + { + $span = 'a long time'; + } + + if ($timestamp <= $local_timestamp) + { + // This is in the past + return $span.' ago'; + } + else + { + // This in the future + return 'in '.$span; + } + } + + /** + * Converts a UNIX timestamp to DOS format. There are very few cases where + * this is needed, but some binary formats use it (eg: zip files.) + * Converting the other direction is done using {@link Date::dos2unix}. + * + * $dos = Date::unix2dos($unix); + * + * @param integer $timestamp UNIX timestamp + * @return integer + */ + public static function unix2dos($timestamp = FALSE) + { + $timestamp = ($timestamp === FALSE) ? getdate() : getdate($timestamp); + + if ($timestamp['year'] < 1980) + { + return (1 << 21 | 1 << 16); + } + + $timestamp['year'] -= 1980; + + // What voodoo is this? I have no idea... Geert can explain it though, + // and that's good enough for me. + return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 | + $timestamp['mday'] << 16 | $timestamp['hours'] << 11 | + $timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1); + } + + /** + * Converts a DOS timestamp to UNIX format.There are very few cases where + * this is needed, but some binary formats use it (eg: zip files.) + * Converting the other direction is done using {@link Date::unix2dos}. + * + * $unix = Date::dos2unix($dos); + * + * @param integer $timestamp DOS timestamp + * @return integer + */ + public static function dos2unix($timestamp = FALSE) + { + $sec = 2 * ($timestamp & 0x1f); + $min = ($timestamp >> 5) & 0x3f; + $hrs = ($timestamp >> 11) & 0x1f; + $day = ($timestamp >> 16) & 0x1f; + $mon = ($timestamp >> 21) & 0x0f; + $year = ($timestamp >> 25) & 0x7f; + + return mktime($hrs, $min, $sec, $mon, $day, $year + 1980); + } + + /** + * Returns a date/time string with the specified timestamp format + * + * $time = Date::formatted_time('5 minutes ago'); + * + * @link http://www.php.net/manual/datetime.construct + * @param string $datetime_str datetime string + * @param string $timestamp_format timestamp format + * @param string $timezone timezone identifier + * @return string + */ + public static function formatted_time($datetime_str = 'now', $timestamp_format = NULL, $timezone = NULL) + { + $timestamp_format = ($timestamp_format == NULL) ? Date::$timestamp_format : $timestamp_format; + $timezone = ($timezone === NULL) ? Date::$timezone : $timezone; + + $tz = new DateTimeZone($timezone ? $timezone : date_default_timezone_get()); + $time = new DateTime($datetime_str, $tz); + + if ($time->getTimeZone()->getName() !== $tz->getName()) + { + $time->setTimeZone($tz); + } + + return $time->format($timestamp_format); + } + +} // End date diff --git a/system/classes/Kohana/Debug.php b/system/classes/Kohana/Debug.php new file mode 100644 index 0000000..9d1efdf --- /dev/null +++ b/system/classes/Kohana/Debug.php @@ -0,0 +1,465 @@ +'.implode("\n", $output).''; + } + + /** + * Returns an HTML string of information about a single variable. + * + * Borrows heavily on concepts from the Debug class of [Nette](http://nettephp.com/). + * + * @param mixed $value variable to dump + * @param integer $length maximum length of strings + * @param integer $level_recursion recursion limit + * @return string + */ + public static function dump($value, $length = 128, $level_recursion = 10) + { + return Debug::_dump($value, $length, $level_recursion); + } + + /** + * Helper for Debug::dump(), handles recursion in arrays and objects. + * + * @param mixed $var variable to dump + * @param integer $length maximum length of strings + * @param integer $limit recursion limit + * @param integer $level current recursion level (internal usage only!) + * @return string + */ + protected static function _dump( & $var, $length = 128, $limit = 10, $level = 0) + { + if ($var === NULL) + { + return 'NULL'; + } + elseif (is_bool($var)) + { + return 'bool '.($var ? 'TRUE' : 'FALSE'); + } + elseif (is_float($var)) + { + return 'float '.$var; + } + elseif (is_resource($var)) + { + if (($type = get_resource_type($var)) === 'stream' AND $meta = stream_get_meta_data($var)) + { + $meta = stream_get_meta_data($var); + + if (isset($meta['uri'])) + { + $file = $meta['uri']; + + if (function_exists('stream_is_local')) + { + // Only exists on PHP >= 5.2.4 + if (stream_is_local($file)) + { + $file = Debug::path($file); + } + } + + return 'resource('.$type.') '.htmlspecialchars($file, ENT_NOQUOTES, Kohana::$charset); + } + } + else + { + return 'resource('.$type.')'; + } + } + elseif (is_string($var)) + { + // Clean invalid multibyte characters. iconv is only invoked + // if there are non ASCII characters in the string, so this + // isn't too much of a hit. + $var = UTF8::clean($var, Kohana::$charset); + + if (UTF8::strlen($var) > $length) + { + // Encode the truncated string + $str = htmlspecialchars(UTF8::substr($var, 0, $length), ENT_NOQUOTES, Kohana::$charset).' …'; + } + else + { + // Encode the string + $str = htmlspecialchars($var, ENT_NOQUOTES, Kohana::$charset); + } + + return 'string('.strlen($var).') "'.$str.'"'; + } + elseif (is_array($var)) + { + $output = array(); + + // Indentation for this variable + $space = str_repeat($s = ' ', $level); + + static $marker; + + if ($marker === NULL) + { + // Make a unique marker + $marker = uniqid("\x00"); + } + + if (empty($var)) + { + // Do nothing + } + elseif (isset($var[$marker])) + { + $output[] = "(\n$space$s*RECURSION*\n$space)"; + } + elseif ($level < $limit) + { + $output[] = "("; + + $var[$marker] = TRUE; + foreach ($var as $key => & $val) + { + if ($key === $marker) continue; + if ( ! is_int($key)) + { + $key = '"'.htmlspecialchars($key, ENT_NOQUOTES, Kohana::$charset).'"'; + } + + $output[] = "$space$s$key => ".Debug::_dump($val, $length, $limit, $level + 1); + } + unset($var[$marker]); + + $output[] = "$space)"; + } + else + { + // Depth too great + $output[] = "(\n$space$s...\n$space)"; + } + + return 'array('.count($var).') '.implode("\n", $output); + } + elseif (is_object($var)) + { + // Copy the object as an array + $array = (array) $var; + + $output = array(); + + // Indentation for this variable + $space = str_repeat($s = ' ', $level); + + $hash = spl_object_hash($var); + + // Objects that are being dumped + static $objects = array(); + + if (empty($var)) + { + // Do nothing + } + elseif (isset($objects[$hash])) + { + $output[] = "{\n$space$s*RECURSION*\n$space}"; + } + elseif ($level < $limit) + { + $output[] = "{"; + + $objects[$hash] = TRUE; + foreach ($array as $key => & $val) + { + if ($key[0] === "\x00") + { + // Determine if the access is protected or protected + $access = ''.(($key[1] === '*') ? 'protected' : 'private').''; + + // Remove the access level from the variable name + $key = substr($key, strrpos($key, "\x00") + 1); + } + else + { + $access = 'public'; + } + + $output[] = "$space$s$access $key => ".Debug::_dump($val, $length, $limit, $level + 1); + } + unset($objects[$hash]); + + $output[] = "$space}"; + } + else + { + // Depth too great + $output[] = "{\n$space$s...\n$space}"; + } + + return 'object '.get_class($var).'('.count($array).') '.implode("\n", $output); + } + else + { + return ''.gettype($var).' '.htmlspecialchars(print_r($var, TRUE), ENT_NOQUOTES, Kohana::$charset); + } + } + + /** + * Removes application, system, modpath, or docroot from a filename, + * replacing them with the plain text equivalents. Useful for debugging + * when you want to display a shorter path. + * + * // Displays SYSPATH/classes/kohana.php + * echo Debug::path(Kohana::find_file('classes', 'kohana')); + * + * @param string $file path to debug + * @return string + */ + public static function path($file) + { + if (strpos($file, APPPATH) === 0) + { + $file = 'APPPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(APPPATH)); + } + elseif (strpos($file, SYSPATH) === 0) + { + $file = 'SYSPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(SYSPATH)); + } + elseif (strpos($file, MODPATH) === 0) + { + $file = 'MODPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(MODPATH)); + } + elseif (strpos($file, DOCROOT) === 0) + { + $file = 'DOCROOT'.DIRECTORY_SEPARATOR.substr($file, strlen(DOCROOT)); + } + + return $file; + } + + /** + * Returns an HTML string, highlighting a specific line of a file, with some + * number of lines padded above and below. + * + * // Highlights the current line of the current file + * echo Debug::source(__FILE__, __LINE__); + * + * @param string $file file to open + * @param integer $line_number line number to highlight + * @param integer $padding number of padding lines + * @return string source of file + * @return FALSE file is unreadable + */ + public static function source($file, $line_number, $padding = 5) + { + if ( ! $file OR ! is_readable($file)) + { + // Continuing will cause errors + return FALSE; + } + + // Open the file and set the line position + $file = fopen($file, 'r'); + $line = 0; + + // Set the reading range + $range = array('start' => $line_number - $padding, 'end' => $line_number + $padding); + + // Set the zero-padding amount for line numbers + $format = '% '.strlen($range['end']).'d'; + + $source = ''; + while (($row = fgets($file)) !== FALSE) + { + // Increment the line number + if (++$line > $range['end']) + break; + + if ($line >= $range['start']) + { + // Make the row safe for output + $row = htmlspecialchars($row, ENT_NOQUOTES, Kohana::$charset); + + // Trim whitespace and sanitize the row + $row = ''.sprintf($format, $line).' '.$row; + + if ($line === $line_number) + { + // Apply highlighting to this row + $row = ''.$row.''; + } + else + { + $row = ''.$row.''; + } + + // Add to the captured source + $source .= $row; + } + } + + // Close the file + fclose($file); + + return '
    '.$source.'
    '; + } + + /** + * Returns an array of HTML strings that represent each step in the backtrace. + * + * // Displays the entire current backtrace + * echo implode('
    ', Debug::trace()); + * + * @param array $trace + * @return string + */ + public static function trace(array $trace = NULL) + { + if ($trace === NULL) + { + // Start a new trace + $trace = debug_backtrace(); + } + + // Non-standard function calls + $statements = array('include', 'include_once', 'require', 'require_once'); + + $output = array(); + foreach ($trace as $step) + { + if ( ! isset($step['function'])) + { + // Invalid trace step + continue; + } + + if (isset($step['file']) AND isset($step['line'])) + { + // Include the source of this step + $source = Debug::source($step['file'], $step['line']); + } + + if (isset($step['file'])) + { + $file = $step['file']; + + if (isset($step['line'])) + { + $line = $step['line']; + } + } + + // function() + $function = $step['function']; + + if (in_array($step['function'], $statements)) + { + if (empty($step['args'])) + { + // No arguments + $args = array(); + } + else + { + // Sanitize the file path + $args = array($step['args'][0]); + } + } + elseif (isset($step['args'])) + { + if ( ! function_exists($step['function']) OR strpos($step['function'], '{closure}') !== FALSE) + { + // Introspection on closures or language constructs in a stack trace is impossible + $params = NULL; + } + else + { + if (isset($step['class'])) + { + if (method_exists($step['class'], $step['function'])) + { + $reflection = new ReflectionMethod($step['class'], $step['function']); + } + else + { + $reflection = new ReflectionMethod($step['class'], '__call'); + } + } + else + { + $reflection = new ReflectionFunction($step['function']); + } + + // Get the function parameters + $params = $reflection->getParameters(); + } + + $args = array(); + + foreach ($step['args'] as $i => $arg) + { + if (isset($params[$i])) + { + // Assign the argument by the parameter name + $args[$params[$i]->name] = $arg; + } + else + { + // Assign the argument by number + $args[$i] = $arg; + } + } + } + + if (isset($step['class'])) + { + // Class->method() or Class::method() + $function = $step['class'].$step['type'].$step['function']; + } + + $output[] = array( + 'function' => $function, + 'args' => isset($args) ? $args : NULL, + 'file' => isset($file) ? $file : NULL, + 'line' => isset($line) ? $line : NULL, + 'source' => isset($source) ? $source : NULL, + ); + + unset($function, $args, $file, $line, $source); + } + + return $output; + } + +} diff --git a/system/classes/Kohana/Encrypt.php b/system/classes/Kohana/Encrypt.php new file mode 100644 index 0000000..46cfe57 --- /dev/null +++ b/system/classes/Kohana/Encrypt.php @@ -0,0 +1,213 @@ +load('encrypt')->$name; + + if ( ! isset($config['key'])) + { + // No default encryption key is provided! + throw new Kohana_Exception('No encryption key is defined in the encryption configuration group: :group', + array(':group' => $name)); + } + + if ( ! isset($config['mode'])) + { + // Add the default mode + $config['mode'] = MCRYPT_MODE_NOFB; + } + + if ( ! isset($config['cipher'])) + { + // Add the default cipher + $config['cipher'] = MCRYPT_RIJNDAEL_128; + } + + // Create a new instance + Encrypt::$instances[$name] = new Encrypt($config['key'], $config['mode'], $config['cipher']); + } + + return Encrypt::$instances[$name]; + } + + /** + * Creates a new mcrypt wrapper. + * + * @param string $key encryption key + * @param string $mode mcrypt mode + * @param string $cipher mcrypt cipher + */ + public function __construct($key, $mode, $cipher) + { + // Find the max length of the key, based on cipher and mode + $size = mcrypt_get_key_size($cipher, $mode); + + if (isset($key[$size])) + { + // Shorten the key to the maximum size + $key = substr($key, 0, $size); + } + + // Store the key, mode, and cipher + $this->_key = $key; + $this->_mode = $mode; + $this->_cipher = $cipher; + + // Store the IV size + $this->_iv_size = mcrypt_get_iv_size($this->_cipher, $this->_mode); + } + + /** + * Encrypts a string and returns an encrypted string that can be decoded. + * + * $data = $encrypt->encode($data); + * + * The encrypted binary data is encoded using [base64](http://php.net/base64_encode) + * to convert it to a string. This string can be stored in a database, + * displayed, and passed using most other means without corruption. + * + * @param string $data data to be encrypted + * @return string + */ + public function encode($data) + { + // Set the rand type if it has not already been set + if (Encrypt::$_rand === NULL) + { + if (Kohana::$is_windows) + { + // Windows only supports the system random number generator + Encrypt::$_rand = MCRYPT_RAND; + } + else + { + if (defined('MCRYPT_DEV_URANDOM')) + { + // Use /dev/urandom + Encrypt::$_rand = MCRYPT_DEV_URANDOM; + } + elseif (defined('MCRYPT_DEV_RANDOM')) + { + // Use /dev/random + Encrypt::$_rand = MCRYPT_DEV_RANDOM; + } + else + { + // Use the system random number generator + Encrypt::$_rand = MCRYPT_RAND; + } + } + } + + if (Encrypt::$_rand === MCRYPT_RAND) + { + // The system random number generator must always be seeded each + // time it is used, or it will not produce true random results + mt_srand(); + } + + // Create a random initialization vector of the proper size for the current cipher + $iv = mcrypt_create_iv($this->_iv_size, Encrypt::$_rand); + + // Encrypt the data using the configured options and generated iv + $data = mcrypt_encrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv); + + // Use base64 encoding to convert to a string + return base64_encode($iv.$data); + } + + /** + * Decrypts an encoded string back to its original value. + * + * $data = $encrypt->decode($data); + * + * @param string $data encoded string to be decrypted + * @return FALSE if decryption fails + * @return string + */ + public function decode($data) + { + // Convert the data back to binary + $data = base64_decode($data, TRUE); + + if ( ! $data) + { + // Invalid base64 data + return FALSE; + } + + // Extract the initialization vector from the data + $iv = substr($data, 0, $this->_iv_size); + + if ($this->_iv_size !== strlen($iv)) + { + // The iv is not the expected size + return FALSE; + } + + // Remove the iv from the data + $data = substr($data, $this->_iv_size); + + // Return the decrypted data, trimming the \0 padding bytes from the end of the data + return rtrim(mcrypt_decrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv), "\0"); + } + +} // End Encrypt diff --git a/system/classes/Kohana/Exception.php b/system/classes/Kohana/Exception.php new file mode 100644 index 0000000..2ffb42a --- /dev/null +++ b/system/classes/Kohana/Exception.php @@ -0,0 +1,3 @@ +execute(); + $feed = $response->body(); + } + elseif (is_file($feed)) + { + // Get file contents + $feed = file_get_contents($feed); + } + + // Load the feed + $feed = simplexml_load_string($feed, 'SimpleXMLElement', LIBXML_NOCDATA); + + // Restore error reporting + error_reporting($error_level); + + // Feed could not be loaded + if ($feed === FALSE) + return array(); + + $namespaces = $feed->getNamespaces(TRUE); + + // Detect the feed type. RSS 1.0/2.0 and Atom 1.0 are supported. + $feed = isset($feed->channel) ? $feed->xpath('//item') : $feed->entry; + + $i = 0; + $items = array(); + + foreach ($feed as $item) + { + if ($limit > 0 AND $i++ === $limit) + break; + $item_fields = (array) $item; + + // get namespaced tags + foreach ($namespaces as $ns) + { + $item_fields += (array) $item->children($ns); + } + $items[] = $item_fields; + } + + return $items; + } + + /** + * Creates a feed from the given parameters. + * + * @param array $info feed information + * @param array $items items to add to the feed + * @param string $encoding define which encoding to use + * @return string + */ + public static function create($info, $items, $encoding = 'UTF-8') + { + $info += array('title' => 'Generated Feed', 'link' => '', 'generator' => 'KohanaPHP'); + + $feed = ''; + $feed = simplexml_load_string($feed); + + foreach ($info as $name => $value) + { + if ($name === 'image') + { + // Create an image element + $image = $feed->channel->addChild('image'); + + if ( ! isset($value['link'], $value['url'], $value['title'])) + { + throw new Kohana_Exception('Feed images require a link, url, and title'); + } + + if (strpos($value['link'], '://') === FALSE) + { + // Convert URIs to URLs + $value['link'] = URL::site($value['link'], 'http'); + } + + if (strpos($value['url'], '://') === FALSE) + { + // Convert URIs to URLs + $value['url'] = URL::site($value['url'], 'http'); + } + + // Create the image elements + $image->addChild('link', $value['link']); + $image->addChild('url', $value['url']); + $image->addChild('title', $value['title']); + } + else + { + if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value))) + { + // Convert timestamps to RFC 822 formatted dates + $value = date('r', $value); + } + elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE) + { + // Convert URIs to URLs + $value = URL::site($value, 'http'); + } + + // Add the info to the channel + $feed->channel->addChild($name, $value); + } + } + + foreach ($items as $item) + { + // Add the item to the channel + $row = $feed->channel->addChild('item'); + + foreach ($item as $name => $value) + { + if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value))) + { + // Convert timestamps to RFC 822 formatted dates + $value = date('r', $value); + } + elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE) + { + // Convert URIs to URLs + $value = URL::site($value, 'http'); + } + + // Add the info to the row + $row->addChild($name, $value); + } + } + + if (function_exists('dom_import_simplexml')) + { + // Convert the feed object to a DOM object + $feed = dom_import_simplexml($feed)->ownerDocument; + + // DOM generates more readable XML + $feed->formatOutput = TRUE; + + // Export the document as XML + $feed = $feed->saveXML(); + } + else + { + // Export the document as XML + $feed = $feed->asXML(); + } + + return $feed; + } + +} // End Feed diff --git a/system/classes/Kohana/File.php b/system/classes/Kohana/File.php new file mode 100644 index 0000000..b4fff2d --- /dev/null +++ b/system/classes/Kohana/File.php @@ -0,0 +1,241 @@ +file($filename); + } + } + + if (ini_get('mime_magic.magicfile') AND function_exists('mime_content_type')) + { + // The mime_content_type function is only useful with a magic file + return mime_content_type($filename); + } + + if ( ! empty($extension)) + { + return File::mime_by_ext($extension); + } + + // Unable to find the mime-type + return FALSE; + } + + /** + * Return the mime type of an extension. + * + * $mime = File::mime_by_ext('png'); // "image/png" + * + * @param string $extension php, pdf, txt, etc + * @return string mime type on success + * @return FALSE on failure + */ + public static function mime_by_ext($extension) + { + // Load all of the mime types + $mimes = Kohana::$config->load('mimes'); + + return isset($mimes[$extension]) ? $mimes[$extension][0] : FALSE; + } + + /** + * Lookup MIME types for a file + * + * @see Kohana_File::mime_by_ext() + * @param string $extension Extension to lookup + * @return array Array of MIMEs associated with the specified extension + */ + public static function mimes_by_ext($extension) + { + // Load all of the mime types + $mimes = Kohana::$config->load('mimes'); + + return isset($mimes[$extension]) ? ( (array) $mimes[$extension]) : array(); + } + + /** + * Lookup file extensions by MIME type + * + * @param string $type File MIME type + * @return array File extensions matching MIME type + */ + public static function exts_by_mime($type) + { + static $types = array(); + + // Fill the static array + if (empty($types)) + { + foreach (Kohana::$config->load('mimes') as $ext => $mimes) + { + foreach ($mimes as $mime) + { + if ($mime == 'application/octet-stream') + { + // octet-stream is a generic binary + continue; + } + + if ( ! isset($types[$mime])) + { + $types[$mime] = array( (string) $ext); + } + elseif ( ! in_array($ext, $types[$mime])) + { + $types[$mime][] = (string) $ext; + } + } + } + } + + return isset($types[$type]) ? $types[$type] : FALSE; + } + + /** + * Lookup a single file extension by MIME type. + * + * @param string $type MIME type to lookup + * @return mixed First file extension matching or false + */ + public static function ext_by_mime($type) + { + return current(File::exts_by_mime($type)); + } + + /** + * Split a file into pieces matching a specific size. Used when you need to + * split large files into smaller pieces for easy transmission. + * + * $count = File::split($file); + * + * @param string $filename file to be split + * @param integer $piece_size size, in MB, for each piece to be + * @return integer The number of pieces that were created + */ + public static function split($filename, $piece_size = 10) + { + // Open the input file + $file = fopen($filename, 'rb'); + + // Change the piece size to bytes + $piece_size = floor($piece_size * 1024 * 1024); + + // Write files in 8k blocks + $block_size = 1024 * 8; + + // Total number of peices + $peices = 0; + + while ( ! feof($file)) + { + // Create another piece + $peices += 1; + + // Create a new file piece + $piece = str_pad($peices, 3, '0', STR_PAD_LEFT); + $piece = fopen($filename.'.'.$piece, 'wb+'); + + // Number of bytes read + $read = 0; + + do + { + // Transfer the data in blocks + fwrite($piece, fread($file, $block_size)); + + // Another block has been read + $read += $block_size; + } + while ($read < $piece_size); + + // Close the piece + fclose($piece); + } + + // Close the file + fclose($file); + + return $peices; + } + + /** + * Join a split file into a whole file. Does the reverse of [File::split]. + * + * $count = File::join($file); + * + * @param string $filename split filename, without .000 extension + * @return integer The number of pieces that were joined. + */ + public static function join($filename) + { + // Open the file + $file = fopen($filename, 'wb+'); + + // Read files in 8k blocks + $block_size = 1024 * 8; + + // Total number of peices + $pieces = 0; + + while (is_file($piece = $filename.'.'.str_pad($pieces + 1, 3, '0', STR_PAD_LEFT))) + { + // Read another piece + $pieces += 1; + + // Open the piece for reading + $piece = fopen($piece, 'rb'); + + while ( ! feof($piece)) + { + // Transfer the data in blocks + fwrite($file, fread($piece, $block_size)); + } + + // Close the peice + fclose($piece); + } + + return $pieces; + } + +} // End file diff --git a/system/classes/Kohana/Form.php b/system/classes/Kohana/Form.php new file mode 100644 index 0000000..6cf529b --- /dev/null +++ b/system/classes/Kohana/Form.php @@ -0,0 +1,434 @@ + 'get')); + * + * // When "file" inputs are present, you must include the "enctype" + * echo Form::open(NULL, array('enctype' => 'multipart/form-data')); + * + * @param mixed $action form action, defaults to the current request URI, or [Request] class to use + * @param array $attributes html attributes + * @return string + * @uses Request::instance + * @uses URL::site + * @uses HTML::attributes + */ + public static function open($action = NULL, array $attributes = NULL) + { + if ($action instanceof Request) + { + // Use the current URI + $action = $action->uri(); + } + + if ( ! $action) + { + // Allow empty form actions (submits back to the current url). + $action = ''; + } + elseif (strpos($action, '://') === FALSE) + { + // Make the URI absolute + $action = URL::site($action); + } + + // Add the form action to the attributes + $attributes['action'] = $action; + + // Only accept the default character set + $attributes['accept-charset'] = Kohana::$charset; + + if ( ! isset($attributes['method'])) + { + // Use POST method + $attributes['method'] = 'post'; + } + + return ''; + } + + /** + * Creates the closing form tag. + * + * echo Form::close(); + * + * @return string + */ + public static function close() + { + return ''; + } + + /** + * Creates a form input. If no type is specified, a "text" type input will + * be returned. + * + * echo Form::input('username', $username); + * + * @param string $name input name + * @param string $value input value + * @param array $attributes html attributes + * @return string + * @uses HTML::attributes + */ + public static function input($name, $value = NULL, array $attributes = NULL) + { + // Set the input name + $attributes['name'] = $name; + + // Set the input value + $attributes['value'] = $value; + + if ( ! isset($attributes['type'])) + { + // Default type is text + $attributes['type'] = 'text'; + } + + return ''; + } + + /** + * Creates a hidden form input. + * + * echo Form::hidden('csrf', $token); + * + * @param string $name input name + * @param string $value input value + * @param array $attributes html attributes + * @return string + * @uses Form::input + */ + public static function hidden($name, $value = NULL, array $attributes = NULL) + { + $attributes['type'] = 'hidden'; + + return Form::input($name, $value, $attributes); + } + + /** + * Creates a password form input. + * + * echo Form::password('password'); + * + * @param string $name input name + * @param string $value input value + * @param array $attributes html attributes + * @return string + * @uses Form::input + */ + public static function password($name, $value = NULL, array $attributes = NULL) + { + $attributes['type'] = 'password'; + + return Form::input($name, $value, $attributes); + } + + /** + * Creates a file upload form input. No input value can be specified. + * + * echo Form::file('image'); + * + * @param string $name input name + * @param array $attributes html attributes + * @return string + * @uses Form::input + */ + public static function file($name, array $attributes = NULL) + { + $attributes['type'] = 'file'; + + return Form::input($name, NULL, $attributes); + } + + /** + * Creates a checkbox form input. + * + * echo Form::checkbox('remember_me', 1, (bool) $remember); + * + * @param string $name input name + * @param string $value input value + * @param boolean $checked checked status + * @param array $attributes html attributes + * @return string + * @uses Form::input + */ + public static function checkbox($name, $value = NULL, $checked = FALSE, array $attributes = NULL) + { + $attributes['type'] = 'checkbox'; + + if ($checked === TRUE) + { + // Make the checkbox active + $attributes[] = 'checked'; + } + + return Form::input($name, $value, $attributes); + } + + /** + * Creates a radio form input. + * + * echo Form::radio('like_cats', 1, $cats); + * echo Form::radio('like_cats', 0, ! $cats); + * + * @param string $name input name + * @param string $value input value + * @param boolean $checked checked status + * @param array $attributes html attributes + * @return string + * @uses Form::input + */ + public static function radio($name, $value = NULL, $checked = FALSE, array $attributes = NULL) + { + $attributes['type'] = 'radio'; + + if ($checked === TRUE) + { + // Make the radio active + $attributes[] = 'checked'; + } + + return Form::input($name, $value, $attributes); + } + + /** + * Creates a textarea form input. + * + * echo Form::textarea('about', $about); + * + * @param string $name textarea name + * @param string $body textarea body + * @param array $attributes html attributes + * @param boolean $double_encode encode existing HTML characters + * @return string + * @uses HTML::attributes + * @uses HTML::chars + */ + public static function textarea($name, $body = '', array $attributes = NULL, $double_encode = TRUE) + { + // Set the input name + $attributes['name'] = $name; + + // Add default rows and cols attributes (required) + $attributes += array('rows' => 10, 'cols' => 50); + + return ''.HTML::chars($body, $double_encode).''; + } + + /** + * Creates a select form input. + * + * echo Form::select('country', $countries, $country); + * + * [!!] Support for multiple selected options was added in v3.0.7. + * + * @param string $name input name + * @param array $options available options + * @param mixed $selected selected option string, or an array of selected options + * @param array $attributes html attributes + * @return string + * @uses HTML::attributes + */ + public static function select($name, array $options = NULL, $selected = NULL, array $attributes = NULL) + { + // Set the input name + $attributes['name'] = $name; + + if (is_array($selected)) + { + // This is a multi-select, god save us! + $attributes[] = 'multiple'; + } + + if ( ! is_array($selected)) + { + if ($selected === NULL) + { + // Use an empty array + $selected = array(); + } + else + { + // Convert the selected options to an array + $selected = array( (string) $selected); + } + } + + if (empty($options)) + { + // There are no options + $options = ''; + } + else + { + foreach ($options as $value => $name) + { + if (is_array($name)) + { + // Create a new optgroup + $group = array('label' => $value); + + // Create a new list of options + $_options = array(); + + foreach ($name as $_value => $_name) + { + // Force value to be string + $_value = (string) $_value; + + // Create a new attribute set for this option + $option = array('value' => $_value); + + if (in_array($_value, $selected)) + { + // This option is selected + $option[] = 'selected'; + } + + // Change the option to the HTML string + $_options[] = ''.HTML::chars($_name, FALSE).''; + } + + // Compile the options into a string + $_options = "\n".implode("\n", $_options)."\n"; + + $options[$value] = ''.$_options.''; + } + else + { + // Force value to be string + $value = (string) $value; + + // Create a new attribute set for this option + $option = array('value' => $value); + + if (in_array($value, $selected)) + { + // This option is selected + $option[] = 'selected'; + } + + // Change the option to the HTML string + $options[$value] = ''.HTML::chars($name, FALSE).''; + } + } + + // Compile the options into a single string + $options = "\n".implode("\n", $options)."\n"; + } + + return ''.$options.''; + } + + /** + * Creates a submit form input. + * + * echo Form::submit(NULL, 'Login'); + * + * @param string $name input name + * @param string $value input value + * @param array $attributes html attributes + * @return string + * @uses Form::input + */ + public static function submit($name, $value, array $attributes = NULL) + { + $attributes['type'] = 'submit'; + + return Form::input($name, $value, $attributes); + } + + /** + * Creates a image form input. + * + * echo Form::image(NULL, NULL, array('src' => 'media/img/login.png')); + * + * @param string $name input name + * @param string $value input value + * @param array $attributes html attributes + * @param boolean $index add index file to URL? + * @return string + * @uses Form::input + */ + public static function image($name, $value, array $attributes = NULL, $index = FALSE) + { + if ( ! empty($attributes['src'])) + { + if (strpos($attributes['src'], '://') === FALSE) + { + // Add the base URL + $attributes['src'] = URL::base($index).$attributes['src']; + } + } + + $attributes['type'] = 'image'; + + return Form::input($name, $value, $attributes); + } + + /** + * Creates a button form input. Note that the body of a button is NOT escaped, + * to allow images and other HTML to be used. + * + * echo Form::button('save', 'Save Profile', array('type' => 'submit')); + * + * @param string $name input name + * @param string $body input value + * @param array $attributes html attributes + * @return string + * @uses HTML::attributes + */ + public static function button($name, $body, array $attributes = NULL) + { + // Set the input name + $attributes['name'] = $name; + + return ''.$body.''; + } + + /** + * Creates a form label. Label text is not automatically translated. + * + * echo Form::label('username', 'Username'); + * + * @param string $input target input + * @param string $text label text + * @param array $attributes html attributes + * @return string + * @uses HTML::attributes + */ + public static function label($input, $text = NULL, array $attributes = NULL) + { + if ($text === NULL) + { + // Use the input name as the text + $text = ucwords(preg_replace('/[\W_]+/', ' ', $input)); + } + + // Set the label target + $attributes['for'] = $input; + + return ''.$text.''; + } + +} // End form diff --git a/system/classes/Kohana/Fragment.php b/system/classes/Kohana/Fragment.php new file mode 100644 index 0000000..3072fc9 --- /dev/null +++ b/system/classes/Kohana/Fragment.php @@ -0,0 +1,147 @@ + cache key + */ + protected static $_caches = array(); + + /** + * Generate the cache key name for a fragment. + * + * $key = Fragment::_cache_key('footer', TRUE); + * + * @param string $name fragment name + * @param boolean $i18n multilingual fragment support + * @return string + * @uses I18n::lang + * @since 3.0.4 + */ + protected static function _cache_key($name, $i18n = NULL) + { + if ($i18n === NULL) + { + // Use the default setting + $i18n = Fragment::$i18n; + } + + // Language prefix for cache key + $i18n = ($i18n === TRUE) ? I18n::lang() : ''; + + // Note: $i18n and $name need to be delimited to prevent naming collisions + return 'Fragment::cache('.$i18n.'+'.$name.')'; + } + + /** + * Load a fragment from cache and display it. Multiple fragments can + * be nested with different life times. + * + * if ( ! Fragment::load('footer')) { + * // Anything that is echo'ed here will be saved + * Fragment::save(); + * } + * + * @param string $name fragment name + * @param integer $lifetime fragment cache lifetime + * @param boolean $i18n multilingual fragment support + * @return boolean + */ + public static function load($name, $lifetime = NULL, $i18n = NULL) + { + // Set the cache lifetime + $lifetime = ($lifetime === NULL) ? Fragment::$lifetime : (int) $lifetime; + + // Get the cache key name + $cache_key = Fragment::_cache_key($name, $i18n); + + if ($fragment = Kohana::cache($cache_key, NULL, $lifetime)) + { + // Display the cached fragment now + echo $fragment; + + return TRUE; + } + else + { + // Start the output buffer + ob_start(); + + // Store the cache key by the buffer level + Fragment::$_caches[ob_get_level()] = $cache_key; + + return FALSE; + } + } + + /** + * Saves the currently open fragment in the cache. + * + * Fragment::save(); + * + * @return void + */ + public static function save() + { + // Get the buffer level + $level = ob_get_level(); + + if (isset(Fragment::$_caches[$level])) + { + // Get the cache key based on the level + $cache_key = Fragment::$_caches[$level]; + + // Delete the cache key, we don't need it anymore + unset(Fragment::$_caches[$level]); + + // Get the output buffer and display it at the same time + $fragment = ob_get_flush(); + + // Cache the fragment + Kohana::cache($cache_key, $fragment); + } + } + + /** + * Delete a cached fragment. + * + * Fragment::delete($key); + * + * @param string $name fragment name + * @param boolean $i18n multilingual fragment support + * @return void + */ + public static function delete($name, $i18n = NULL) + { + // Invalid the cache + Kohana::cache(Fragment::_cache_key($name, $i18n), NULL, -3600); + } + +} // End Fragment diff --git a/system/classes/Kohana/HTML.php b/system/classes/Kohana/HTML.php new file mode 100644 index 0000000..171856c --- /dev/null +++ b/system/classes/Kohana/HTML.php @@ -0,0 +1,345 @@ +'.$title.''; + } + + /** + * Creates an HTML anchor to a file. Note that the title is not escaped, + * to allow HTML elements within links (images, etc). + * + * echo HTML::file_anchor('media/doc/user_guide.pdf', 'User Guide'); + * + * @param string $file name of file to link to + * @param string $title link text + * @param array $attributes HTML anchor attributes + * @param mixed $protocol protocol to pass to URL::base() + * @param boolean $index include the index page + * @return string + * @uses URL::base + * @uses HTML::attributes + */ + public static function file_anchor($file, $title = NULL, array $attributes = NULL, $protocol = NULL, $index = FALSE) + { + if ($title === NULL) + { + // Use the file name as the title + $title = basename($file); + } + + // Add the file link to the attributes + $attributes['href'] = URL::site($file, $protocol, $index); + + return ''.$title.''; + } + + /** + * Creates an email (mailto:) anchor. Note that the title is not escaped, + * to allow HTML elements within links (images, etc). + * + * echo HTML::mailto($address); + * + * @param string $email email address to send to + * @param string $title link text + * @param array $attributes HTML anchor attributes + * @return string + * @uses HTML::attributes + */ + public static function mailto($email, $title = NULL, array $attributes = NULL) + { + if ($title === NULL) + { + // Use the email address as the title + $title = $email; + } + + return ''.$title.''; + } + + /** + * Creates a style sheet link element. + * + * echo HTML::style('media/css/screen.css'); + * + * @param string $file file name + * @param array $attributes default attributes + * @param mixed $protocol protocol to pass to URL::base() + * @param boolean $index include the index page + * @return string + * @uses URL::base + * @uses HTML::attributes + */ + public static function style($file, array $attributes = NULL, $protocol = NULL, $index = FALSE) + { + if (strpos($file, '://') === FALSE) + { + // Add the base URL + $file = URL::site($file, $protocol, $index); + } + + // Set the stylesheet link + $attributes['href'] = $file; + + // Set the stylesheet rel + $attributes['rel'] = empty($attributes['rel']) ? 'stylesheet' : $attributes['rel']; + + // Set the stylesheet type + $attributes['type'] = 'text/css'; + + return ''; + } + + /** + * Creates a script link. + * + * echo HTML::script('media/js/jquery.min.js'); + * + * @param string $file file name + * @param array $attributes default attributes + * @param mixed $protocol protocol to pass to URL::base() + * @param boolean $index include the index page + * @return string + * @uses URL::base + * @uses HTML::attributes + */ + public static function script($file, array $attributes = NULL, $protocol = NULL, $index = FALSE) + { + if (strpos($file, '://') === FALSE) + { + // Add the base URL + $file = URL::site($file, $protocol, $index); + } + + // Set the script link + $attributes['src'] = $file; + + // Set the script type + $attributes['type'] = 'text/javascript'; + + return ''; + } + + /** + * Creates a image link. + * + * echo HTML::image('media/img/logo.png', array('alt' => 'My Company')); + * + * @param string $file file name + * @param array $attributes default attributes + * @param mixed $protocol protocol to pass to URL::base() + * @param boolean $index include the index page + * @return string + * @uses URL::base + * @uses HTML::attributes + */ + public static function image($file, array $attributes = NULL, $protocol = NULL, $index = FALSE) + { + if (strpos($file, '://') === FALSE) + { + // Add the base URL + $file = URL::site($file, $protocol, $index); + } + + // Add the image link + $attributes['src'] = $file; + + return ''; + } + + /** + * Compiles an array of HTML attributes into an attribute string. + * Attributes will be sorted using HTML::$attribute_order for consistency. + * + * echo ''.$content.''; + * + * @param array $attributes attribute list + * @return string + */ + public static function attributes(array $attributes = NULL) + { + if (empty($attributes)) + return ''; + + $sorted = array(); + foreach (HTML::$attribute_order as $key) + { + if (isset($attributes[$key])) + { + // Add the attribute to the sorted list + $sorted[$key] = $attributes[$key]; + } + } + + // Combine the sorted attributes + $attributes = $sorted + $attributes; + + $compiled = ''; + foreach ($attributes as $key => $value) + { + if ($value === NULL) + { + // Skip attributes that have NULL values + continue; + } + + if (is_int($key)) + { + // Assume non-associative keys are mirrored attributes + $key = $value; + + if ( ! HTML::$strict) + { + // Just use a key + $value = FALSE; + } + } + + // Add the attribute key + $compiled .= ' '.$key; + + if ($value OR HTML::$strict) + { + // Add the attribute value + $compiled .= '="'.HTML::chars($value).'"'; + } + } + + return $compiled; + } + +} // End html diff --git a/system/classes/Kohana/HTTP.php b/system/classes/Kohana/HTTP.php new file mode 100644 index 0000000..2dec59f --- /dev/null +++ b/system/classes/Kohana/HTTP.php @@ -0,0 +1,217 @@ + $code + )); + + throw $e->location($uri); + } + + /** + * Checks the browser cache to see the response needs to be returned, + * execution will halt and a 304 Not Modified will be sent if the + * browser cache is up to date. + * + * @param Request $request Request + * @param Response $response Response + * @param string $etag Resource ETag + * @throws HTTP_Exception_304 + * @return Response + */ + public static function check_cache(Request $request, Response $response, $etag = NULL) + { + // Generate an etag if necessary + if ($etag == NULL) + { + $etag = $response->generate_etag(); + } + + // Set the ETag header + $response->headers('etag', $etag); + + // Add the Cache-Control header if it is not already set + // This allows etags to be used with max-age, etc + if ($response->headers('cache-control')) + { + $response->headers('cache-control', $response->headers('cache-control').', must-revalidate'); + } + else + { + $response->headers('cache-control', 'must-revalidate'); + } + + // Check if we have a matching etag + if ($request->headers('if-none-match') AND (string) $request->headers('if-none-match') === $etag) + { + // No need to send data again + throw HTTP_Exception::factory(304)->headers('etag', $etag); + } + + return $response; + } + + /** + * Parses a HTTP header string into an associative array + * + * @param string $header_string Header string to parse + * @return HTTP_Header + */ + public static function parse_header_string($header_string) + { + // If the PECL HTTP extension is loaded + if (extension_loaded('http')) + { + // Use the fast method to parse header string + return new HTTP_Header(http_parse_headers($header_string)); + } + + // Otherwise we use the slower PHP parsing + $headers = array(); + + // Match all HTTP headers + if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header_string, $matches)) + { + // Parse each matched header + foreach ($matches[0] as $key => $value) + { + // If the header has not already been set + if ( ! isset($headers[$matches[1][$key]])) + { + // Apply the header directly + $headers[$matches[1][$key]] = $matches[2][$key]; + } + // Otherwise there is an existing entry + else + { + // If the entry is an array + if (is_array($headers[$matches[1][$key]])) + { + // Apply the new entry to the array + $headers[$matches[1][$key]][] = $matches[2][$key]; + } + // Otherwise create a new array with the entries + else + { + $headers[$matches[1][$key]] = array( + $headers[$matches[1][$key]], + $matches[2][$key], + ); + } + } + } + } + + // Return the headers + return new HTTP_Header($headers); + } + + /** + * Parses the the HTTP request headers and returns an array containing + * key value pairs. This method is slow, but provides an accurate + * representation of the HTTP request. + * + * // Get http headers into the request + * $request->headers = HTTP::request_headers(); + * + * @return HTTP_Header + */ + public static function request_headers() + { + // If running on apache server + if (function_exists('apache_request_headers')) + { + // Return the much faster method + return new HTTP_Header(apache_request_headers()); + } + // If the PECL HTTP tools are installed + elseif (extension_loaded('http')) + { + // Return the much faster method + return new HTTP_Header(http_get_request_headers()); + } + + // Setup the output + $headers = array(); + + // Parse the content type + if ( ! empty($_SERVER['CONTENT_TYPE'])) + { + $headers['content-type'] = $_SERVER['CONTENT_TYPE']; + } + + // Parse the content length + if ( ! empty($_SERVER['CONTENT_LENGTH'])) + { + $headers['content-length'] = $_SERVER['CONTENT_LENGTH']; + } + + foreach ($_SERVER as $key => $value) + { + // If there is no HTTP header here, skip + if (strpos($key, 'HTTP_') !== 0) + { + continue; + } + + // This is a dirty hack to ensure HTTP_X_FOO_BAR becomes x-foo-bar + $headers[str_replace(array('HTTP_', '_'), array('', '-'), $key)] = $value; + } + + return new HTTP_Header($headers); + } + + /** + * Processes an array of key value pairs and encodes + * the values to meet RFC 3986 + * + * @param array $params Params + * @return string + */ + public static function www_form_urlencode(array $params = array()) + { + if ( ! $params) + return; + + $encoded = array(); + + foreach ($params as $key => $value) + { + $encoded[] = $key.'='.rawurlencode($value); + } + + return implode('&', $encoded); + } +} // End Kohana_HTTP \ No newline at end of file diff --git a/system/classes/Kohana/HTTP/Exception.php b/system/classes/Kohana/HTTP/Exception.php new file mode 100644 index 0000000..0c878d7 --- /dev/null +++ b/system/classes/Kohana/HTTP/Exception.php @@ -0,0 +1,72 @@ + $user)); + * + * @param string $message status message, custom content to display with error + * @param array $variables translation variables + * @return void + */ + public function __construct($message = NULL, array $variables = NULL, Exception $previous = NULL) + { + parent::__construct($message, $variables, $this->_code, $previous); + } + + /** + * Store the Request that triggered this exception. + * + * @param Request $request Request object that triggered this exception. + * @return Response + */ + public function request(Request $request = NULL) + { + if ($request === NULL) + return $this->_request; + + $this->_request = $request; + + return $this; + } + + /** + * Generate a Response for the current Exception + * + * @uses Kohana_Exception::response() + * @return Response + */ + public function get_response() + { + return Kohana_Exception::response($this); + } + +} // End Kohana_HTTP_Exception \ No newline at end of file diff --git a/system/classes/Kohana/HTTP/Exception/300.php b/system/classes/Kohana/HTTP/Exception/300.php new file mode 100644 index 0000000..cc5a450 --- /dev/null +++ b/system/classes/Kohana/HTTP/Exception/300.php @@ -0,0 +1,10 @@ +headers('Location'); + + $this->headers('Location', $uri); + + return $this; + } + + /** + * Validate this exception contains everything needed to continue. + * + * @throws Kohana_Exception + * @return bool + */ + public function check() + { + if ($location = $this->headers('location') === NULL) + throw new Kohana_Exception('A \'location\' must be specified for a redirect'); + + if (strpos($location, '://') === FALSE) + throw new Kohana_Exception('An absolute URI to the proxy server must be specified'); + + return TRUE; + } +} \ No newline at end of file diff --git a/system/classes/Kohana/HTTP/Exception/307.php b/system/classes/Kohana/HTTP/Exception/307.php new file mode 100644 index 0000000..1294f27 --- /dev/null +++ b/system/classes/Kohana/HTTP/Exception/307.php @@ -0,0 +1,10 @@ +headers('www-authenticate'); + + $this->headers('www-authenticate', $challenge); + + return $this; + } + + /** + * Validate this exception contains everything needed to continue. + * + * @throws Kohana_Exception + * @return bool + */ + public function check() + { + if ($this->headers('www-authenticate') === NULL) + throw new Kohana_Exception('A \'www-authenticate\' header must be specified for a HTTP 401 Unauthorized'); + + return TRUE; + } +} diff --git a/system/classes/Kohana/HTTP/Exception/402.php b/system/classes/Kohana/HTTP/Exception/402.php new file mode 100644 index 0000000..54d9ff1 --- /dev/null +++ b/system/classes/Kohana/HTTP/Exception/402.php @@ -0,0 +1,10 @@ +headers('allow', $methods); + + return $this; + } + + /** + * Validate this exception contains everything needed to continue. + * + * @throws Kohana_Exception + * @return bool + */ + public function check() + { + if ($location = $this->headers('allow') === NULL) + throw new Kohana_Exception('A list of allowed methods must be specified'); + + return TRUE; + } + +} \ No newline at end of file diff --git a/system/classes/Kohana/HTTP/Exception/406.php b/system/classes/Kohana/HTTP/Exception/406.php new file mode 100644 index 0000000..b23861a --- /dev/null +++ b/system/classes/Kohana/HTTP/Exception/406.php @@ -0,0 +1,10 @@ + $user)); + * + * @param string $message status message, custom content to display with error + * @param array $variables translation variables + * @return void + */ + public function __construct($message = NULL, array $variables = NULL, Exception $previous = NULL) + { + parent::__construct($message, $variables, $previous); + + // Prepare our response object and set the correct status code. + $this->_response = Response::factory() + ->status($this->_code); + } + + /** + * Gets and sets headers to the [Response]. + * + * @see [Response::headers] + * @param mixed $key + * @param string $value + * @return mixed + */ + public function headers($key = NULL, $value = NULL) + { + $result = $this->_response->headers($key, $value); + + if ( ! $result instanceof Response) + return $result; + + return $this; + } + + /** + * Validate this exception contains everything needed to continue. + * + * @throws Kohana_Exception + * @return bool + */ + public function check() + { + return TRUE; + } + + /** + * Generate a Response for the current Exception + * + * @uses Kohana_Exception::response() + * @return Response + */ + public function get_response() + { + $this->check(); + + return $this->_response; + } + +} // End Kohana_HTTP_Exception \ No newline at end of file diff --git a/system/classes/Kohana/HTTP/Exception/Redirect.php b/system/classes/Kohana/HTTP/Exception/Redirect.php new file mode 100644 index 0000000..e065289 --- /dev/null +++ b/system/classes/Kohana/HTTP/Exception/Redirect.php @@ -0,0 +1,51 @@ +headers('Location'); + + if (strpos($uri, '://') === FALSE) + { + // Make the URI into a URL + $uri = URL::site($uri, TRUE, ! empty(Kohana::$index_file)); + } + + $this->headers('Location', $uri); + + return $this; + } + + /** + * Validate this exception contains everything needed to continue. + * + * @throws Kohana_Exception + * @return bool + */ + public function check() + { + if ($this->headers('location') === NULL) + throw new Kohana_Exception('A \'location\' must be specified for a redirect'); + + return TRUE; + } + +} // End Kohana_HTTP_Exception_Redirect \ No newline at end of file diff --git a/system/classes/Kohana/HTTP/Header.php b/system/classes/Kohana/HTTP/Header.php new file mode 100644 index 0000000..5850537 --- /dev/null +++ b/system/classes/Kohana/HTTP/Header.php @@ -0,0 +1,949 @@ + array('*' => (float) HTTP_Header::DEFAULT_QUALITY)); + + // Parse the accept header qualities + $accepts = HTTP_Header::accept_quality($accepts); + + $parsed_accept = array(); + + // This method of iteration uses less resource + $keys = array_keys($accepts); + foreach ($keys as $key) + { + // Extract the parts + $parts = explode('/', $key, 2); + + // Invalid content type- bail + if ( ! isset($parts[1])) + continue; + + // Set the parsed output + $parsed_accept[$parts[0]][$parts[1]] = $accepts[$key]; + } + + return $parsed_accept; + } + + /** + * Parses the `Accept-Charset:` HTTP header and returns an array containing + * the charset and associated quality. + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2 + * @param string $charset charset string to parse + * @return array + * @since 3.2.0 + */ + public static function parse_charset_header($charset = NULL) + { + if ($charset === NULL) + { + return array('*' => (float) HTTP_Header::DEFAULT_QUALITY); + } + + return HTTP_Header::accept_quality(explode(',', (string) $charset)); + } + + /** + * Parses the `Accept-Encoding:` HTTP header and returns an array containing + * the charsets and associated quality. + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 + * @param string $encoding charset string to parse + * @return array + * @since 3.2.0 + */ + public static function parse_encoding_header($encoding = NULL) + { + // Accept everything + if ($encoding === NULL) + { + return array('*' => (float) HTTP_Header::DEFAULT_QUALITY); + } + elseif ($encoding === '') + { + return array('identity' => (float) HTTP_Header::DEFAULT_QUALITY); + } + else + { + return HTTP_Header::accept_quality(explode(',', (string) $encoding)); + } + } + + /** + * Parses the `Accept-Language:` HTTP header and returns an array containing + * the languages and associated quality. + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + * @param string $language charset string to parse + * @return array + * @since 3.2.0 + */ + public static function parse_language_header($language = NULL) + { + if ($language === NULL) + { + return array('*' => array('*' => (float) HTTP_Header::DEFAULT_QUALITY)); + } + + $language = HTTP_Header::accept_quality(explode(',', (string) $language)); + + $parsed_language = array(); + + $keys = array_keys($language); + foreach ($keys as $key) + { + // Extract the parts + $parts = explode('-', $key, 2); + + // Invalid content type- bail + if ( ! isset($parts[1])) + { + $parsed_language[$parts[0]]['*'] = $language[$key]; + } + else + { + // Set the parsed output + $parsed_language[$parts[0]][$parts[1]] = $language[$key]; + } + } + + return $parsed_language; + } + + /** + * Generates a Cache-Control HTTP header based on the supplied array. + * + * // Set the cache control headers you want to use + * $cache_control = array( + * 'max-age' => 3600, + * 'must-revalidate', + * 'public' + * ); + * + * // Create the cache control header, creates : + * // cache-control: max-age=3600, must-revalidate, public + * $response->headers('Cache-Control', HTTP_Header::create_cache_control($cache_control); + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13 + * @param array $cache_control Cache-Control to render to string + * @return string + */ + public static function create_cache_control(array $cache_control) + { + $parts = array(); + + foreach ($cache_control as $key => $value) + { + $parts[] = (is_int($key)) ? $value : ($key.'='.$value); + } + + return implode(', ', $parts); + } + + /** + * Parses the Cache-Control header and returning an array representation of the Cache-Control + * header. + * + * // Create the cache control header + * $response->headers('cache-control', 'max-age=3600, must-revalidate, public'); + * + * // Parse the cache control header + * if ($cache_control = HTTP_Header::parse_cache_control($response->headers('cache-control'))) + * { + * // Cache-Control header was found + * $maxage = $cache_control['max-age']; + * } + * + * @param array $cache_control Array of headers + * @return mixed + */ + public static function parse_cache_control($cache_control) + { + $directives = explode(',', strtolower($cache_control)); + + if ($directives === FALSE) + return FALSE; + + $output = array(); + + foreach ($directives as $directive) + { + if (strpos($directive, '=') !== FALSE) + { + list($key, $value) = explode('=', trim($directive), 2); + + $output[$key] = ctype_digit($value) ? (int) $value : $value; + } + else + { + $output[] = trim($directive); + } + } + + return $output; + } + + /** + * @var array Accept: (content) types + */ + protected $_accept_content; + + /** + * @var array Accept-Charset: parsed header + */ + protected $_accept_charset; + + /** + * @var array Accept-Encoding: parsed header + */ + protected $_accept_encoding; + + /** + * @var array Accept-Language: parsed header + */ + protected $_accept_language; + + /** + * Constructor method for [Kohana_HTTP_Header]. Uses the standard constructor + * of the parent `ArrayObject` class. + * + * $header_object = new HTTP_Header(array('x-powered-by' => 'Kohana 3.1.x', 'expires' => '...')); + * + * @param mixed $input Input array + * @param int $flags Flags + * @param string $iterator_class The iterator class to use + */ + public function __construct(array $input = array(), $flags = NULL, $iterator_class = 'ArrayIterator') + { + /** + * @link http://www.w3.org/Protocols/rfc2616/rfc2616.html + * + * HTTP header declarations should be treated as case-insensitive + */ + $input = array_change_key_case( (array) $input, CASE_LOWER); + + parent::__construct($input, $flags, $iterator_class); + } + + /** + * Returns the header object as a string, including + * the terminating new line + * + * // Return the header as a string + * echo (string) $request->headers(); + * + * @return string + */ + public function __toString() + { + $header = ''; + + foreach ($this as $key => $value) + { + // Put the keys back the Case-Convention expected + $key = Text::ucfirst($key); + + if (is_array($value)) + { + $header .= $key.': '.(implode(', ', $value))."\r\n"; + } + else + { + $header .= $key.': '.$value."\r\n"; + } + } + + return $header."\r\n"; + } + + /** + * Overloads `ArrayObject::offsetSet()` to enable handling of header + * with multiple instances of the same directive. If the `$replace` flag + * is `FALSE`, the header will be appended rather than replacing the + * original setting. + * + * @param mixed $index index to set `$newval` to + * @param mixed $newval new value to set + * @param boolean $replace replace existing value + * @return void + * @since 3.2.0 + */ + public function offsetSet($index, $newval, $replace = TRUE) + { + // Ensure the index is lowercase + $index = strtolower($index); + + if ($replace OR ! $this->offsetExists($index)) + { + return parent::offsetSet($index, $newval); + } + + $current_value = $this->offsetGet($index); + + if (is_array($current_value)) + { + $current_value[] = $newval; + } + else + { + $current_value = array($current_value, $newval); + } + + return parent::offsetSet($index, $current_value); + } + + /** + * Overloads the `ArrayObject::offsetExists()` method to ensure keys + * are lowercase. + * + * @param string $index + * @return boolean + * @since 3.2.0 + */ + public function offsetExists($index) + { + return parent::offsetExists(strtolower($index)); + } + + /** + * Overloads the `ArrayObject::offsetUnset()` method to ensure keys + * are lowercase. + * + * @param string $index + * @return void + * @since 3.2.0 + */ + public function offsetUnset($index) + { + return parent::offsetUnset(strtolower($index)); + } + + /** + * Overload the `ArrayObject::offsetGet()` method to ensure that all + * keys passed to it are formatted correctly for this object. + * + * @param string $index index to retrieve + * @return mixed + * @since 3.2.0 + */ + public function offsetGet($index) + { + return parent::offsetGet(strtolower($index)); + } + + /** + * Overloads the `ArrayObject::exchangeArray()` method to ensure that + * all keys are changed to lowercase. + * + * @param mixed $input + * @return array + * @since 3.2.0 + */ + public function exchangeArray($input) + { + /** + * @link http://www.w3.org/Protocols/rfc2616/rfc2616.html + * + * HTTP header declarations should be treated as case-insensitive + */ + $input = array_change_key_case( (array) $input, CASE_LOWER); + + return parent::exchangeArray($input); + } + + /** + * Parses a HTTP Message header line and applies it to this HTTP_Header + * + * $header = $response->headers(); + * $header->parse_header_string(NULL, 'content-type: application/json'); + * + * @param resource $resource the resource (required by Curl API) + * @param string $header_line the line from the header to parse + * @return int + * @since 3.2.0 + */ + public function parse_header_string($resource, $header_line) + { + $headers = array(); + + if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header_line, $matches)) + { + foreach ($matches[0] as $key => $value) + { + $this->offsetSet($matches[1][$key], $matches[2][$key], FALSE); + } + } + + return strlen($header_line); + } + + /** + * Returns the accept quality of a submitted mime type based on the + * request `Accept:` header. If the `$explicit` argument is `TRUE`, + * only precise matches will be returned, excluding all wildcard (`*`) + * directives. + * + * // Accept: application/xml; application/json; q=.5; text/html; q=.2, text/* + * // Accept quality for application/json + * + * // $quality = 0.5 + * $quality = $request->headers()->accepts_at_quality('application/json'); + * + * // $quality_explicit = FALSE + * $quality_explicit = $request->headers()->accepts_at_quality('text/plain', TRUE); + * + * @param string $type + * @param boolean $explicit explicit check, excludes `*` + * @return mixed + * @since 3.2.0 + */ + public function accepts_at_quality($type, $explicit = FALSE) + { + // Parse Accept header if required + if ($this->_accept_content === NULL) + { + if ($this->offsetExists('Accept')) + { + $accept = $this->offsetGet('Accept'); + } + else + { + $accept = '*/*'; + } + + $this->_accept_content = HTTP_Header::parse_accept_header($accept); + } + + // If not a real mime, try and find it in config + if (strpos($type, '/') === FALSE) + { + $mime = Kohana::$config->load('mimes.'.$type); + + if ($mime === NULL) + return FALSE; + + $quality = FALSE; + + foreach ($mime as $_type) + { + $quality_check = $this->accepts_at_quality($_type, $explicit); + $quality = ($quality_check > $quality) ? $quality_check : $quality; + } + + return $quality; + } + + $parts = explode('/', $type, 2); + + if (isset($this->_accept_content[$parts[0]][$parts[1]])) + { + return $this->_accept_content[$parts[0]][$parts[1]]; + } + elseif ($explicit === TRUE) + { + return FALSE; + } + else + { + if (isset($this->_accept_content[$parts[0]]['*'])) + { + return $this->_accept_content[$parts[0]]['*']; + } + elseif (isset($this->_accept_content['*']['*'])) + { + return $this->_accept_content['*']['*']; + } + else + { + return FALSE; + } + } + } + + /** + * Returns the preferred response content type based on the accept header + * quality settings. If items have the same quality value, the first item + * found in the array supplied as `$types` will be returned. + * + * // Get the preferred acceptable content type + * // Accept: text/html, application/json; q=.8, text/* + * $result = $header->preferred_accept(array( + * 'text/html' + * 'text/rtf', + * 'application/json' + * )); // $result = 'application/json' + * + * $result = $header->preferred_accept(array( + * 'text/rtf', + * 'application/xml' + * ), TRUE); // $result = FALSE (none matched explicitly) + * + * + * @param array $types the content types to examine + * @param boolean $explicit only allow explicit references, no wildcards + * @return string name of the preferred content type + * @since 3.2.0 + */ + public function preferred_accept(array $types, $explicit = FALSE) + { + $preferred = FALSE; + $ceiling = 0; + + foreach ($types as $type) + { + $quality = $this->accepts_at_quality($type, $explicit); + + if ($quality > $ceiling) + { + $preferred = $type; + $ceiling = $quality; + } + } + + return $preferred; + } + + /** + * Returns the quality of the supplied `$charset` argument. This method + * will automatically parse the `Accept-Charset` header if present and + * return the associated resolved quality value. + * + * // Accept-Charset: utf-8, utf-16; q=.8, iso-8859-1; q=.5 + * $quality = $header->accepts_charset_at_quality('utf-8'); + * // $quality = (float) 1 + * + * @param string $charset charset to examine + * @return float the quality of the charset + * @since 3.2.0 + */ + public function accepts_charset_at_quality($charset) + { + if ($this->_accept_charset === NULL) + { + if ($this->offsetExists('Accept-Charset')) + { + $charset_header = strtolower($this->offsetGet('Accept-Charset')); + $this->_accept_charset = HTTP_Header::parse_charset_header($charset_header); + } + else + { + $this->_accept_charset = HTTP_Header::parse_charset_header(NULL); + } + } + + $charset = strtolower($charset); + + if (isset($this->_accept_charset[$charset])) + { + return $this->_accept_charset[$charset]; + } + elseif (isset($this->_accept_charset['*'])) + { + return $this->_accept_charset['*']; + } + elseif ($charset === 'iso-8859-1') + { + return (float) 1; + } + + return (float) 0; + } + + /** + * Returns the preferred charset from the supplied array `$charsets` based + * on the `Accept-Charset` header directive. + * + * // Accept-Charset: utf-8, utf-16; q=.8, iso-8859-1; q=.5 + * $charset = $header->preferred_charset(array( + * 'utf-10', 'ascii', 'utf-16', 'utf-8' + * )); // $charset = 'utf-8' + * + * @param array $charsets charsets to test + * @return mixed preferred charset or `FALSE` + * @since 3.2.0 + */ + public function preferred_charset(array $charsets) + { + $preferred = FALSE; + $ceiling = 0; + + foreach ($charsets as $charset) + { + $quality = $this->accepts_charset_at_quality($charset); + + if ($quality > $ceiling) + { + $preferred = $charset; + $ceiling = $quality; + } + } + + return $preferred; + } + + /** + * Returns the quality of the `$encoding` type passed to it. Encoding + * is usually compression such as `gzip`, but could be some other + * message encoding algorithm. This method allows explicit checks to be + * done ignoring wildcards. + * + * // Accept-Encoding: compress, gzip, *; q=.5 + * $encoding = $header->accepts_encoding_at_quality('gzip'); + * // $encoding = (float) 1.0s + * + * @param string $encoding encoding type to interrogate + * @param boolean $explicit explicit check, ignoring wildcards and `identity` + * @return float + * @since 3.2.0 + */ + public function accepts_encoding_at_quality($encoding, $explicit = FALSE) + { + if ($this->_accept_encoding === NULL) + { + if ($this->offsetExists('Accept-Encoding')) + { + $encoding_header = $this->offsetGet('Accept-Encoding'); + } + else + { + $encoding_header = NULL; + } + + $this->_accept_encoding = HTTP_Header::parse_encoding_header($encoding_header); + } + + // Normalize the encoding + $encoding = strtolower($encoding); + + if (isset($this->_accept_encoding[$encoding])) + { + return $this->_accept_encoding[$encoding]; + } + + if ($explicit === FALSE) + { + if (isset($this->_accept_encoding['*'])) + { + return $this->_accept_encoding['*']; + } + elseif ($encoding === 'identity') + { + return (float) HTTP_Header::DEFAULT_QUALITY; + } + } + + return (float) 0; + } + + /** + * Returns the preferred message encoding type based on quality, and can + * optionally ignore wildcard references. If two or more encodings have the + * same quality, the first listed in `$encodings` will be returned. + * + * // Accept-Encoding: compress, gzip, *; q.5 + * $encoding = $header->preferred_encoding(array( + * 'gzip', 'bzip', 'blowfish' + * )); + * // $encoding = 'gzip'; + * + * @param array $encodings encodings to test against + * @param boolean $explicit explicit check, if `TRUE` wildcards are excluded + * @return mixed + * @since 3.2.0 + */ + public function preferred_encoding(array $encodings, $explicit = FALSE) + { + $ceiling = 0; + $preferred = FALSE; + + foreach ($encodings as $encoding) + { + $quality = $this->accepts_encoding_at_quality($encoding, $explicit); + + if ($quality > $ceiling) + { + $ceiling = $quality; + $preferred = $encoding; + } + } + + return $preferred; + } + + /** + * Returns the quality of `$language` supplied, optionally ignoring + * wildcards if `$explicit` is set to a non-`FALSE` value. If the quality + * is not found, `0.0` is returned. + * + * // Accept-Language: en-us, en-gb; q=.7, en; q=.5 + * $lang = $header->accepts_language_at_quality('en-gb'); + * // $lang = (float) 0.7 + * + * $lang2 = $header->accepts_language_at_quality('en-au'); + * // $lang2 = (float) 0.5 + * + * $lang3 = $header->accepts_language_at_quality('en-au', TRUE); + * // $lang3 = (float) 0.0 + * + * @param string $language language to interrogate + * @param boolean $explicit explicit interrogation, `TRUE` ignores wildcards + * @return float + * @since 3.2.0 + */ + public function accepts_language_at_quality($language, $explicit = FALSE) + { + if ($this->_accept_language === NULL) + { + if ($this->offsetExists('Accept-Language')) + { + $language_header = strtolower($this->offsetGet('Accept-Language')); + } + else + { + $language_header = NULL; + } + + $this->_accept_language = HTTP_Header::parse_language_header($language_header); + } + + // Normalize the language + $language_parts = explode('-', strtolower($language), 2); + + if (isset($this->_accept_language[$language_parts[0]])) + { + if (isset($language_parts[1])) + { + if (isset($this->_accept_language[$language_parts[0]][$language_parts[1]])) + { + return $this->_accept_language[$language_parts[0]][$language_parts[1]]; + } + elseif ($explicit === FALSE AND isset($this->_accept_language[$language_parts[0]]['*'])) + { + return $this->_accept_language[$language_parts[0]]['*']; + } + } + elseif (isset($this->_accept_language[$language_parts[0]]['*'])) + { + return $this->_accept_language[$language_parts[0]]['*']; + } + } + + if ($explicit === FALSE AND isset($this->_accept_language['*'])) + { + return $this->_accept_language['*']; + } + + return (float) 0; + } + + /** + * Returns the preferred language from the supplied array `$languages` based + * on the `Accept-Language` header directive. + * + * // Accept-Language: en-us, en-gb; q=.7, en; q=.5 + * $lang = $header->preferred_language(array( + * 'en-gb', 'en-au', 'fr', 'es' + * )); // $lang = 'en-gb' + * + * @param array $languages + * @param boolean $explicit + * @return mixed + * @since 3.2.0 + */ + public function preferred_language(array $languages, $explicit = FALSE) + { + $ceiling = 0; + $preferred = FALSE; + + foreach ($languages as $language) + { + $quality = $this->accepts_language_at_quality($language, $explicit); + + if ($quality > $ceiling) + { + $ceiling = $quality; + $preferred = $language; + } + } + + return $preferred; + } + + /** + * Sends headers to the php processor, or supplied `$callback` argument. + * This method formats the headers correctly for output, re-instating their + * capitalization for transmission. + * + * [!!] if you supply a custom header handler via `$callback`, it is + * recommended that `$response` is returned + * + * @param HTTP_Response $response header to send + * @param boolean $replace replace existing value + * @param callback $callback optional callback to replace PHP header function + * @return mixed + * @since 3.2.0 + */ + public function send_headers(HTTP_Response $response = NULL, $replace = FALSE, $callback = NULL) + { + if ($response === NULL) + { + // Default to the initial request message + $response = Request::initial()->response(); + } + + $protocol = $response->protocol(); + $status = $response->status(); + + // Create the response header + $processed_headers = array($protocol.' '.$status.' '.Response::$messages[$status]); + + // Get the headers array + $headers = $response->headers()->getArrayCopy(); + + foreach ($headers as $header => $value) + { + if (is_array($value)) + { + $value = implode(', ', $value); + } + + $processed_headers[] = Text::ucfirst($header).': '.$value; + } + + if ( ! isset($headers['content-type'])) + { + $processed_headers[] = 'Content-Type: '.Kohana::$content_type.'; charset='.Kohana::$charset; + } + + if (Kohana::$expose AND ! isset($headers['x-powered-by'])) + { + $processed_headers[] = 'X-Powered-By: '.Kohana::version(); + } + + // Get the cookies and apply + if ($cookies = $response->cookie()) + { + $processed_headers['Set-Cookie'] = $cookies; + } + + if (is_callable($callback)) + { + // Use the callback method to set header + return call_user_func($callback, $response, $processed_headers, $replace); + } + else + { + $this->_send_headers_to_php($processed_headers, $replace); + return $response; + } + } + + /** + * Sends the supplied headers to the PHP output buffer. If cookies + * are included in the message they will be handled appropriately. + * + * @param array $headers headers to send to php + * @param boolean $replace replace existing headers + * @return self + * @since 3.2.0 + */ + protected function _send_headers_to_php(array $headers, $replace) + { + // If the headers have been sent, get out + if (headers_sent()) + return $this; + + foreach ($headers as $key => $line) + { + if ($key == 'Set-Cookie' AND is_array($line)) + { + // Send cookies + foreach ($line as $name => $value) + { + Cookie::set($name, $value['value'], $value['expiration']); + } + + continue; + } + + header($line, $replace); + } + + return $this; + } + +} // End Kohana_HTTP_Header diff --git a/system/classes/Kohana/HTTP/Message.php b/system/classes/Kohana/HTTP/Message.php new file mode 100644 index 0000000..096a20a --- /dev/null +++ b/system/classes/Kohana/HTTP/Message.php @@ -0,0 +1,56 @@ +status(404); + * + * // Get the current status + * $status = $response->status(); + * + * @param integer $code Status to set to this response + * @return mixed + */ + public function status($code = NULL); + +} \ No newline at end of file diff --git a/system/classes/Kohana/I18n.php b/system/classes/Kohana/I18n.php new file mode 100644 index 0000000..0856da0 --- /dev/null +++ b/system/classes/Kohana/I18n.php @@ -0,0 +1,166 @@ + $username)); + * + * @package Kohana + * @category Base + * @author Kohana Team + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_I18n { + + /** + * @var string target language: en-us, es-es, zh-cn, etc + */ + public static $lang = 'en-us'; + + /** + * @var string source language: en-us, es-es, zh-cn, etc + */ + public static $source = 'en-us'; + + /** + * @var array cache of loaded languages + */ + protected static $_cache = array(); + + /** + * Get and set the target language. + * + * // Get the current language + * $lang = I18n::lang(); + * + * // Change the current language to Spanish + * I18n::lang('es-es'); + * + * @param string $lang new language setting + * @return string + * @since 3.0.2 + */ + public static function lang($lang = NULL) + { + if ($lang) + { + // Normalize the language + I18n::$lang = strtolower(str_replace(array(' ', '_'), '-', $lang)); + } + + return I18n::$lang; + } + + /** + * Returns translation of a string. If no translation exists, the original + * string will be returned. No parameters are replaced. + * + * $hello = I18n::get('Hello friends, my name is :name'); + * + * @param string $string text to translate + * @param string $lang target language + * @return string + */ + public static function get($string, $lang = NULL) + { + if ( ! $lang) + { + // Use the global target language + $lang = I18n::$lang; + } + + // Load the translation table for this language + $table = I18n::load($lang); + + // Return the translated string if it exists + return isset($table[$string]) ? $table[$string] : $string; + } + + /** + * Returns the translation table for a given language. + * + * // Get all defined Spanish messages + * $messages = I18n::load('es-es'); + * + * @param string $lang language to load + * @return array + */ + public static function load($lang) + { + if (isset(I18n::$_cache[$lang])) + { + return I18n::$_cache[$lang]; + } + + // New translation table + $table = array(); + + // Split the language: language, region, locale, etc + $parts = explode('-', $lang); + + do + { + // Create a path for this set of parts + $path = implode(DIRECTORY_SEPARATOR, $parts); + + if ($files = Kohana::find_file('i18n', $path, NULL, TRUE)) + { + $t = array(); + foreach ($files as $file) + { + // Merge the language strings into the sub table + $t = array_merge($t, Kohana::load($file)); + } + + // Append the sub table, preventing less specific language + // files from overloading more specific files + $table += $t; + } + + // Remove the last part + array_pop($parts); + } + while ($parts); + + // Cache the translation table locally + return I18n::$_cache[$lang] = $table; + } + +} // End I18n + +if ( ! function_exists('__')) +{ + /** + * Kohana translation/internationalization function. The PHP function + * [strtr](http://php.net/strtr) is used for replacing parameters. + * + * __('Welcome back, :user', array(':user' => $username)); + * + * [!!] The target language is defined by [I18n::$lang]. + * + * @uses I18n::get + * @param string $string text to translate + * @param array $values values to replace in the translated text + * @param string $lang source language + * @return string + */ + function __($string, array $values = NULL, $lang = 'en-us') + { + if ($lang !== I18n::$lang) + { + // The message and target languages are different + // Get the translation for this message + $string = I18n::get($string); + } + + return empty($values) ? $string : strtr($string, $values); + } +} diff --git a/system/classes/Kohana/Inflector.php b/system/classes/Kohana/Inflector.php new file mode 100644 index 0000000..2d7c2df --- /dev/null +++ b/system/classes/Kohana/Inflector.php @@ -0,0 +1,269 @@ +load('inflector')->uncountable; + + // Make uncountables mirrored + Inflector::$uncountable = array_combine(Inflector::$uncountable, Inflector::$uncountable); + } + + return isset(Inflector::$uncountable[strtolower($str)]); + } + + /** + * Makes a plural word singular. + * + * echo Inflector::singular('cats'); // "cat" + * echo Inflector::singular('fish'); // "fish", uncountable + * + * You can also provide the count to make inflection more intelligent. + * In this case, it will only return the singular value if the count is + * greater than one and not zero. + * + * echo Inflector::singular('cats', 2); // "cats" + * + * [!!] Special inflections are defined in `config/inflector.php`. + * + * @param string $str word to singularize + * @param integer $count count of thing + * @return string + * @uses Inflector::uncountable + */ + public static function singular($str, $count = NULL) + { + // $count should always be a float + $count = ($count === NULL) ? 1.0 : (float) $count; + + // Do nothing when $count is not 1 + if ($count != 1) + return $str; + + // Remove garbage + $str = strtolower(trim($str)); + + // Cache key name + $key = 'singular_'.$str.$count; + + if (isset(Inflector::$cache[$key])) + return Inflector::$cache[$key]; + + if (Inflector::uncountable($str)) + return Inflector::$cache[$key] = $str; + + if (empty(Inflector::$irregular)) + { + // Cache irregular words + Inflector::$irregular = Kohana::$config->load('inflector')->irregular; + } + + if ($irregular = array_search($str, Inflector::$irregular)) + { + $str = $irregular; + } + elseif (preg_match('/us$/', $str)) + { + // http://en.wikipedia.org/wiki/Plural_form_of_words_ending_in_-us + // Already singular, do nothing + } + elseif (preg_match('/[sxz]es$/', $str) OR preg_match('/[^aeioudgkprt]hes$/', $str)) + { + // Remove "es" + $str = substr($str, 0, -2); + } + elseif (preg_match('/[^aeiou]ies$/', $str)) + { + // Replace "ies" with "y" + $str = substr($str, 0, -3).'y'; + } + elseif (substr($str, -1) === 's' AND substr($str, -2) !== 'ss') + { + // Remove singular "s" + $str = substr($str, 0, -1); + } + + return Inflector::$cache[$key] = $str; + } + + /** + * Makes a singular word plural. + * + * echo Inflector::plural('fish'); // "fish", uncountable + * echo Inflector::plural('cat'); // "cats" + * + * You can also provide the count to make inflection more intelligent. + * In this case, it will only return the plural value if the count is + * not one. + * + * echo Inflector::singular('cats', 3); // "cats" + * + * [!!] Special inflections are defined in `config/inflector.php`. + * + * @param string $str word to pluralize + * @param integer $count count of thing + * @return string + * @uses Inflector::uncountable + */ + public static function plural($str, $count = NULL) + { + // $count should always be a float + $count = ($count === NULL) ? 0.0 : (float) $count; + + // Do nothing with singular + if ($count == 1) + return $str; + + // Remove garbage + $str = trim($str); + + // Cache key name + $key = 'plural_'.$str.$count; + + // Check uppercase + $is_uppercase = ctype_upper($str); + + if (isset(Inflector::$cache[$key])) + return Inflector::$cache[$key]; + + if (Inflector::uncountable($str)) + return Inflector::$cache[$key] = $str; + + if (empty(Inflector::$irregular)) + { + // Cache irregular words + Inflector::$irregular = Kohana::$config->load('inflector')->irregular; + } + + if (isset(Inflector::$irregular[$str])) + { + $str = Inflector::$irregular[$str]; + } + elseif (preg_match('/[sxz]$/', $str) OR preg_match('/[^aeioudgkprt]h$/', $str)) + { + $str .= 'es'; + } + elseif (preg_match('/[^aeiou]y$/', $str)) + { + // Change "y" to "ies" + $str = substr_replace($str, 'ies', -1); + } + else + { + $str .= 's'; + } + + // Convert to uppsecase if nessasary + if ($is_uppercase) + { + $str = strtoupper($str); + } + + // Set the cache and return + return Inflector::$cache[$key] = $str; + } + + /** + * Makes a phrase camel case. Spaces and underscores will be removed. + * + * $str = Inflector::camelize('mother cat'); // "motherCat" + * $str = Inflector::camelize('kittens in bed'); // "kittensInBed" + * + * @param string $str phrase to camelize + * @return string + */ + public static function camelize($str) + { + $str = 'x'.strtolower(trim($str)); + $str = ucwords(preg_replace('/[\s_]+/', ' ', $str)); + + return substr(str_replace(' ', '', $str), 1); + } + + /** + * Converts a camel case phrase into a spaced phrase. + * + * $str = Inflector::decamelize('houseCat'); // "house cat" + * $str = Inflector::decamelize('kingAllyCat'); // "king ally cat" + * + * @param string $str phrase to camelize + * @param string $sep word separator + * @return string + */ + public static function decamelize($str, $sep = ' ') + { + return strtolower(preg_replace('/([a-z])([A-Z])/', '$1'.$sep.'$2', trim($str))); + } + + /** + * Makes a phrase underscored instead of spaced. + * + * $str = Inflector::underscore('five cats'); // "five_cats"; + * + * @param string $str phrase to underscore + * @return string + */ + public static function underscore($str) + { + return preg_replace('/\s+/', '_', trim($str)); + } + + /** + * Makes an underscored or dashed phrase human-readable. + * + * $str = Inflector::humanize('kittens-are-cats'); // "kittens are cats" + * $str = Inflector::humanize('dogs_as_well'); // "dogs as well" + * + * @param string $str phrase to make human-readable + * @return string + */ + public static function humanize($str) + { + return preg_replace('/[_-]+/', ' ', trim($str)); + } + +} // End Inflector diff --git a/system/classes/Kohana/Kohana/Exception.php b/system/classes/Kohana/Kohana/Exception.php new file mode 100644 index 0000000..2955bec --- /dev/null +++ b/system/classes/Kohana/Kohana/Exception.php @@ -0,0 +1,282 @@ + human readable name + */ + public static $php_errors = array( + E_ERROR => 'Fatal Error', + E_USER_ERROR => 'User Error', + E_PARSE => 'Parse Error', + E_WARNING => 'Warning', + E_USER_WARNING => 'User Warning', + E_STRICT => 'Strict', + E_NOTICE => 'Notice', + E_RECOVERABLE_ERROR => 'Recoverable Error', + E_DEPRECATED => 'Deprecated', + ); + + /** + * @var string error rendering view + */ + public static $error_view = 'kohana/error'; + + /** + * @var string error view content type + */ + public static $error_view_content_type = 'text/html'; + + /** + * Creates a new translated exception. + * + * throw new Kohana_Exception('Something went terrible wrong, :user', + * array(':user' => $user)); + * + * @param string $message error message + * @param array $variables translation variables + * @param integer|string $code the exception code + * @param Exception $previous Previous exception + * @return void + */ + public function __construct($message = "", array $variables = NULL, $code = 0, Exception $previous = NULL) + { + // Set the message + $message = __($message, $variables); + + // Pass the message and integer code to the parent + parent::__construct($message, (int) $code, $previous); + + // Save the unmodified code + // @link http://bugs.php.net/39615 + $this->code = $code; + } + + /** + * Magic object-to-string method. + * + * echo $exception; + * + * @uses Kohana_Exception::text + * @return string + */ + public function __toString() + { + return Kohana_Exception::text($this); + } + + /** + * Inline exception handler, displays the error message, source of the + * exception, and the stack trace of the error. + * + * @uses Kohana_Exception::response + * @param Exception $e + * @return boolean + */ + public static function handler(Exception $e) + { + $response = Kohana_Exception::_handler($e); + + // Send the response to the browser + echo $response->send_headers()->body(); + + exit(1); + } + + /** + * Exception handler, logs the exception and generates a Response object + * for display. + * + * @uses Kohana_Exception::response + * @param Exception $e + * @return boolean + */ + public static function _handler(Exception $e) + { + try + { + // Log the exception + Kohana_Exception::log($e); + + // Generate the response + $response = Kohana_Exception::response($e); + + return $response; + } + catch (Exception $e) + { + /** + * Things are going *really* badly for us, We now have no choice + * but to bail. Hard. + */ + // Clean the output buffer if one exists + ob_get_level() AND ob_clean(); + + // Set the Status code to 500, and Content-Type to text/plain. + header('Content-Type: text/plain; charset='.Kohana::$charset, TRUE, 500); + + echo Kohana_Exception::text($e); + + exit(1); + } + } + + /** + * Logs an exception. + * + * @uses Kohana_Exception::text + * @param Exception $e + * @param int $level + * @return void + */ + public static function log(Exception $e, $level = Log::EMERGENCY) + { + if (is_object(Kohana::$log)) + { + // Create a text version of the exception + $error = Kohana_Exception::text($e); + + // Add this exception to the log + Kohana::$log->add($level, $error, NULL, array('exception' => $e)); + + // Make sure the logs are written + Kohana::$log->write(); + } + } + + /** + * Get a single line of text representing the exception: + * + * Error [ Code ]: Message ~ File [ Line ] + * + * @param Exception $e + * @return string + */ + public static function text(Exception $e) + { + return sprintf('%s [ %s ]: %s ~ %s [ %d ]', + get_class($e), $e->getCode(), strip_tags($e->getMessage()), Debug::path($e->getFile()), $e->getLine()); + } + + /** + * Get a Response object representing the exception + * + * @uses Kohana_Exception::text + * @param Exception $e + * @return Response + */ + public static function response(Exception $e) + { + try + { + // Get the exception information + $class = get_class($e); + $code = $e->getCode(); + $message = $e->getMessage(); + $file = $e->getFile(); + $line = $e->getLine(); + $trace = $e->getTrace(); + + if ( ! headers_sent()) + { + // Make sure the proper http header is sent + $http_header_status = ($e instanceof HTTP_Exception) ? $code : 500; + } + + /** + * HTTP_Exceptions are constructed in the HTTP_Exception::factory() + * method. We need to remove that entry from the trace and overwrite + * the variables from above. + */ + if ($e instanceof HTTP_Exception AND $trace[0]['function'] == 'factory') + { + extract(array_shift($trace)); + } + + + if ($e instanceof ErrorException) + { + /** + * If XDebug is installed, and this is a fatal error, + * use XDebug to generate the stack trace + */ + if (function_exists('xdebug_get_function_stack') AND $code == E_ERROR) + { + $trace = array_slice(array_reverse(xdebug_get_function_stack()), 4); + + foreach ($trace as & $frame) + { + /** + * XDebug pre 2.1.1 doesn't currently set the call type key + * http://bugs.xdebug.org/view.php?id=695 + */ + if ( ! isset($frame['type'])) + { + $frame['type'] = '??'; + } + + // XDebug also has a different name for the parameters array + if (isset($frame['params']) AND ! isset($frame['args'])) + { + $frame['args'] = $frame['params']; + } + } + } + + if (isset(Kohana_Exception::$php_errors[$code])) + { + // Use the human-readable error name + $code = Kohana_Exception::$php_errors[$code]; + } + } + + /** + * The stack trace becomes unmanageable inside PHPUnit. + * + * The error view ends up several GB in size, taking + * serveral minutes to render. + */ + if (defined('PHPUnit_MAIN_METHOD')) + { + $trace = array_slice($trace, 0, 2); + } + + // Instantiate the error view. + $view = View::factory(Kohana_Exception::$error_view, get_defined_vars()); + + // Prepare the response object. + $response = Response::factory(); + + // Set the response status + $response->status(($e instanceof HTTP_Exception) ? $e->getCode() : 500); + + // Set the response headers + $response->headers('Content-Type', Kohana_Exception::$error_view_content_type.'; charset='.Kohana::$charset); + + // Set the response body + $response->body($view->render()); + } + catch (Exception $e) + { + /** + * Things are going badly for us, Lets try to keep things under control by + * generating a simpler response object. + */ + $response = Response::factory(); + $response->status(500); + $response->headers('Content-Type', 'text/plain'); + $response->body(Kohana_Exception::text($e)); + } + + return $response; + } + +} // End Kohana_Exception diff --git a/system/classes/Kohana/Log.php b/system/classes/Kohana/Log.php new file mode 100644 index 0000000..d06b07c --- /dev/null +++ b/system/classes/Kohana/Log.php @@ -0,0 +1,228 @@ +attach($writer); + * + * @param Log_Writer $writer instance + * @param mixed $levels array of messages levels to write OR max level to write + * @param integer $min_level min level to write IF $levels is not an array + * @return Log + */ + public function attach(Log_Writer $writer, $levels = array(), $min_level = 0) + { + if ( ! is_array($levels)) + { + $levels = range($min_level, $levels); + } + + $this->_writers["{$writer}"] = array + ( + 'object' => $writer, + 'levels' => $levels + ); + + return $this; + } + + /** + * Detaches a log writer. The same writer object must be used. + * + * $log->detach($writer); + * + * @param Log_Writer $writer instance + * @return Log + */ + public function detach(Log_Writer $writer) + { + // Remove the writer + unset($this->_writers["{$writer}"]); + + return $this; + } + + /** + * Adds a message to the log. Replacement values must be passed in to be + * replaced using [strtr](http://php.net/strtr). + * + * $log->add(Log::ERROR, 'Could not locate user: :user', array( + * ':user' => $username, + * )); + * + * @param string $level level of message + * @param string $message message body + * @param array $values values to replace in the message + * @param array $additional additional custom parameters to supply to the log writer + * @return Log + */ + public function add($level, $message, array $values = NULL, array $additional = NULL) + { + if ($values) + { + // Insert the values into the message + $message = strtr($message, $values); + } + + // Grab a copy of the trace + if (isset($additional['exception'])) + { + $trace = $additional['exception']->getTrace(); + } + else + { + // Older php version don't have 'DEBUG_BACKTRACE_IGNORE_ARGS', so manually remove the args from the backtrace + if ( ! defined('DEBUG_BACKTRACE_IGNORE_ARGS')) + { + $trace = array_map(function ($item) { + unset($item['args']); + return $item; + }, array_slice(debug_backtrace(FALSE), 1)); + } + else + { + $trace = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 1); + } + } + + if ($additional == NULL) + { + $additional = array(); + } + + // Create a new message + $this->_messages[] = array + ( + 'time' => time(), + 'level' => $level, + 'body' => $message, + 'trace' => $trace, + 'file' => isset($trace[0]['file']) ? $trace[0]['file'] : NULL, + 'line' => isset($trace[0]['line']) ? $trace[0]['line'] : NULL, + 'class' => isset($trace[0]['class']) ? $trace[0]['class'] : NULL, + 'function' => isset($trace[0]['function']) ? $trace[0]['function'] : NULL, + 'additional' => $additional, + ); + + if (Log::$write_on_add) + { + // Write logs as they are added + $this->write(); + } + + return $this; + } + + /** + * Write and clear all of the messages. + * + * $log->write(); + * + * @return void + */ + public function write() + { + if (empty($this->_messages)) + { + // There is nothing to write, move along + return; + } + + // Import all messages locally + $messages = $this->_messages; + + // Reset the messages array + $this->_messages = array(); + + foreach ($this->_writers as $writer) + { + if (empty($writer['levels'])) + { + // Write all of the messages + $writer['object']->write($messages); + } + else + { + // Filtered messages + $filtered = array(); + + foreach ($messages as $message) + { + if (in_array($message['level'], $writer['levels'])) + { + // Writer accepts this kind of message + $filtered[] = $message; + } + } + + // Write the filtered messages + $writer['object']->write($filtered); + } + } + } + +} // End Kohana_Log diff --git a/system/classes/Kohana/Log/File.php b/system/classes/Kohana/Log/File.php new file mode 100644 index 0000000..f603cf2 --- /dev/null +++ b/system/classes/Kohana/Log/File.php @@ -0,0 +1,94 @@ + Debug::path($directory))); + } + + // Determine the directory path + $this->_directory = realpath($directory).DIRECTORY_SEPARATOR; + } + + /** + * Writes each of the messages into the log file. The log file will be + * appended to the `YYYY/MM/DD.log.php` file, where YYYY is the current + * year, MM is the current month, and DD is the current day. + * + * $writer->write($messages); + * + * @param array $messages + * @return void + */ + public function write(array $messages) + { + // Set the yearly directory name + $directory = $this->_directory.date('Y'); + + if ( ! is_dir($directory)) + { + // Create the yearly directory + mkdir($directory, 02777); + + // Set permissions (must be manually set to fix umask issues) + chmod($directory, 02777); + } + + // Add the month to the directory + $directory .= DIRECTORY_SEPARATOR.date('m'); + + if ( ! is_dir($directory)) + { + // Create the monthly directory + mkdir($directory, 02777); + + // Set permissions (must be manually set to fix umask issues) + chmod($directory, 02777); + } + + // Set the name of the log file + $filename = $directory.DIRECTORY_SEPARATOR.date('d').EXT; + + if ( ! file_exists($filename)) + { + // Create the log file + file_put_contents($filename, Kohana::FILE_SECURITY.' ?>'.PHP_EOL); + + // Allow anyone to write to log files + chmod($filename, 0666); + } + + foreach ($messages as $message) + { + // Write each message into the log file + file_put_contents($filename, PHP_EOL.$this->format_message($message), FILE_APPEND); + } + } + +} // End Kohana_Log_File \ No newline at end of file diff --git a/system/classes/Kohana/Log/StdErr.php b/system/classes/Kohana/Log/StdErr.php new file mode 100644 index 0000000..6e6486f --- /dev/null +++ b/system/classes/Kohana/Log/StdErr.php @@ -0,0 +1,29 @@ +write($messages); + * + * @param array $messages + * @return void + */ + public function write(array $messages) + { + foreach ($messages as $message) + { + // Writes out each message + fwrite(STDERR, $this->format_message($message).PHP_EOL); + } + } + +} // End Kohana_Log_StdErr diff --git a/system/classes/Kohana/Log/StdOut.php b/system/classes/Kohana/Log/StdOut.php new file mode 100644 index 0000000..3fb5e1d --- /dev/null +++ b/system/classes/Kohana/Log/StdOut.php @@ -0,0 +1,30 @@ +write($messages); + * + * @param array $messages + * @return void + */ + public function write(array $messages) + { + foreach ($messages as $message) + { + // Writes out each message + fwrite(STDOUT, $this->format_message($message).PHP_EOL); + } + } + +} // End Kohana_Log_StdOut diff --git a/system/classes/Kohana/Log/Syslog.php b/system/classes/Kohana/Log/Syslog.php new file mode 100644 index 0000000..809629c --- /dev/null +++ b/system/classes/Kohana/Log/Syslog.php @@ -0,0 +1,65 @@ +_ident = $ident; + + // Open the connection to syslog + openlog($this->_ident, LOG_CONS, $facility); + } + + /** + * Writes each of the messages into the syslog. + * + * @param array $messages + * @return void + */ + public function write(array $messages) + { + foreach ($messages as $message) + { + syslog($message['level'], $message['body']); + + if (isset($message['additional']['exception'])) + { + syslog(Log_Writer::$strace_level, $message['additional']['exception']->getTraceAsString()); + } + } + } + + /** + * Closes the syslog connection + * + * @return void + */ + public function __destruct() + { + // Close connection to syslog + closelog(); + } + +} // End Kohana_Log_Syslog diff --git a/system/classes/Kohana/Log/Writer.php b/system/classes/Kohana/Log/Writer.php new file mode 100644 index 0000000..4449958 --- /dev/null +++ b/system/classes/Kohana/Log/Writer.php @@ -0,0 +1,95 @@ + 'EMERGENCY', + LOG_ALERT => 'ALERT', + LOG_CRIT => 'CRITICAL', + LOG_ERR => 'ERROR', + LOG_WARNING => 'WARNING', + LOG_NOTICE => 'NOTICE', + LOG_INFO => 'INFO', + LOG_DEBUG => 'DEBUG', + ); + + /** + * @var int Level to use for stack traces + */ + public static $strace_level = LOG_DEBUG; + + /** + * Write an array of messages. + * + * $writer->write($messages); + * + * @param array $messages + * @return void + */ + abstract public function write(array $messages); + + /** + * Allows the writer to have a unique key when stored. + * + * echo $writer; + * + * @return string + */ + final public function __toString() + { + return spl_object_hash($this); + } + + /** + * Formats a log entry. + * + * @param array $message + * @param string $format + * @return string + */ + public function format_message(array $message, $format = "time --- level: body in file:line") + { + $message['time'] = Date::formatted_time('@'.$message['time'], Log_Writer::$timestamp, Log_Writer::$timezone, TRUE); + $message['level'] = $this->_log_levels[$message['level']]; + + $string = strtr($format, $message); + + if (isset($message['additional']['exception'])) + { + // Re-use as much as possible, just resetting the body to the trace + $message['body'] = $message['additional']['exception']->getTraceAsString(); + $message['level'] = $this->_log_levels[Log_Writer::$strace_level]; + + $string .= PHP_EOL.strtr($format, $message); + } + + return $string; + } + +} // End Kohana_Log_Writer diff --git a/system/classes/Kohana/Model.php b/system/classes/Kohana/Model.php new file mode 100644 index 0000000..0baf9d1 --- /dev/null +++ b/system/classes/Kohana/Model.php @@ -0,0 +1,29 @@ + power of 2 that defines the unit's size + */ + public static $byte_units = array + ( + 'B' => 0, + 'K' => 10, + 'Ki' => 10, + 'KB' => 10, + 'KiB' => 10, + 'M' => 20, + 'Mi' => 20, + 'MB' => 20, + 'MiB' => 20, + 'G' => 30, + 'Gi' => 30, + 'GB' => 30, + 'GiB' => 30, + 'T' => 40, + 'Ti' => 40, + 'TB' => 40, + 'TiB' => 40, + 'P' => 50, + 'Pi' => 50, + 'PB' => 50, + 'PiB' => 50, + 'E' => 60, + 'Ei' => 60, + 'EB' => 60, + 'EiB' => 60, + 'Z' => 70, + 'Zi' => 70, + 'ZB' => 70, + 'ZiB' => 70, + 'Y' => 80, + 'Yi' => 80, + 'YB' => 80, + 'YiB' => 80, + ); + + /** + * Returns the English ordinal suffix (th, st, nd, etc) of a number. + * + * echo 2, Num::ordinal(2); // "2nd" + * echo 10, Num::ordinal(10); // "10th" + * echo 33, Num::ordinal(33); // "33rd" + * + * @param integer $number + * @return string + */ + public static function ordinal($number) + { + if ($number % 100 > 10 AND $number % 100 < 14) + { + return 'th'; + } + + switch ($number % 10) + { + case 1: + return 'st'; + case 2: + return 'nd'; + case 3: + return 'rd'; + default: + return 'th'; + } + } + + /** + * Locale-aware number and monetary formatting. + * + * // In English, "1,200.05" + * // In Spanish, "1200,05" + * // In Portuguese, "1 200,05" + * echo Num::format(1200.05, 2); + * + * // In English, "1,200.05" + * // In Spanish, "1.200,05" + * // In Portuguese, "1.200.05" + * echo Num::format(1200.05, 2, TRUE); + * + * @param float $number number to format + * @param integer $places decimal places + * @param boolean $monetary monetary formatting? + * @return string + * @since 3.0.2 + */ + public static function format($number, $places, $monetary = FALSE) + { + $info = localeconv(); + + if ($monetary) + { + $decimal = $info['mon_decimal_point']; + $thousands = $info['mon_thousands_sep']; + } + else + { + $decimal = $info['decimal_point']; + $thousands = $info['thousands_sep']; + } + + return number_format($number, $places, $decimal, $thousands); + } + + /** + * Round a number to a specified precision, using a specified tie breaking technique + * + * @param float $value Number to round + * @param integer $precision Desired precision + * @param integer $mode Tie breaking mode, accepts the PHP_ROUND_HALF_* constants + * @param boolean $native Set to false to force use of the userland implementation + * @return float Rounded number + */ + public static function round($value, $precision = 0, $mode = self::ROUND_HALF_UP, $native = TRUE) + { + if (version_compare(PHP_VERSION, '5.3', '>=') AND $native) + { + return round($value, $precision, $mode); + } + + if ($mode === self::ROUND_HALF_UP) + { + return round($value, $precision); + } + else + { + $factor = ($precision === 0) ? 1 : pow(10, $precision); + + switch ($mode) + { + case self::ROUND_HALF_DOWN: + case self::ROUND_HALF_EVEN: + case self::ROUND_HALF_ODD: + // Check if we have a rounding tie, otherwise we can just call round() + if (($value * $factor) - floor($value * $factor) === 0.5) + { + if ($mode === self::ROUND_HALF_DOWN) + { + // Round down operation, so we round down unless the value + // is -ve because up is down and down is up down there. ;) + $up = ($value < 0); + } + else + { + // Round up if the integer is odd and the round mode is set to even + // or the integer is even and the round mode is set to odd. + // Any other instance round down. + $up = ( ! ( ! (floor($value * $factor) & 1)) === ($mode === self::ROUND_HALF_EVEN)); + } + + if ($up) + { + $value = ceil($value * $factor); + } + else + { + $value = floor($value * $factor); + } + return $value / $factor; + } + else + { + return round($value, $precision); + } + break; + } + } + } + + /** + * Converts a file size number to a byte value. File sizes are defined in + * the format: SB, where S is the size (1, 8.5, 300, etc.) and B is the + * byte unit (K, MiB, GB, etc.). All valid byte units are defined in + * Num::$byte_units + * + * echo Num::bytes('200K'); // 204800 + * echo Num::bytes('5MiB'); // 5242880 + * echo Num::bytes('1000'); // 1000 + * echo Num::bytes('2.5GB'); // 2684354560 + * + * @param string $bytes file size in SB format + * @return float + */ + public static function bytes($size) + { + // Prepare the size + $size = trim( (string) $size); + + // Construct an OR list of byte units for the regex + $accepted = implode('|', array_keys(Num::$byte_units)); + + // Construct the regex pattern for verifying the size format + $pattern = '/^([0-9]+(?:\.[0-9]+)?)('.$accepted.')?$/Di'; + + // Verify the size format and store the matching parts + if ( ! preg_match($pattern, $size, $matches)) + throw new Kohana_Exception('The byte unit size, ":size", is improperly formatted.', array( + ':size' => $size, + )); + + // Find the float value of the size + $size = (float) $matches[1]; + + // Find the actual unit, assume B if no unit specified + $unit = Arr::get($matches, 2, 'B'); + + // Convert the size into bytes + $bytes = $size * pow(2, Num::$byte_units[$unit]); + + return $bytes; + } + +} // End num diff --git a/system/classes/Kohana/Profiler.php b/system/classes/Kohana/Profiler.php new file mode 100644 index 0000000..fc03511 --- /dev/null +++ b/system/classes/Kohana/Profiler.php @@ -0,0 +1,385 @@ + strtolower($group), + 'name' => (string) $name, + + // Start the benchmark + 'start_time' => microtime(TRUE), + 'start_memory' => memory_get_usage(), + + // Set the stop keys without values + 'stop_time' => FALSE, + 'stop_memory' => FALSE, + ); + + return $token; + } + + /** + * Stops a benchmark. + * + * Profiler::stop($token); + * + * @param string $token + * @return void + */ + public static function stop($token) + { + // Stop the benchmark + Profiler::$_marks[$token]['stop_time'] = microtime(TRUE); + Profiler::$_marks[$token]['stop_memory'] = memory_get_usage(); + } + + /** + * Deletes a benchmark. If an error occurs during the benchmark, it is + * recommended to delete the benchmark to prevent statistics from being + * adversely affected. + * + * Profiler::delete($token); + * + * @param string $token + * @return void + */ + public static function delete($token) + { + // Remove the benchmark + unset(Profiler::$_marks[$token]); + } + + /** + * Returns all the benchmark tokens by group and name as an array. + * + * $groups = Profiler::groups(); + * + * @return array + */ + public static function groups() + { + $groups = array(); + + foreach (Profiler::$_marks as $token => $mark) + { + // Sort the tokens by the group and name + $groups[$mark['group']][$mark['name']][] = $token; + } + + return $groups; + } + + /** + * Gets the min, max, average and total of a set of tokens as an array. + * + * $stats = Profiler::stats($tokens); + * + * @param array $tokens profiler tokens + * @return array min, max, average, total + * @uses Profiler::total + */ + public static function stats(array $tokens) + { + // Min and max are unknown by default + $min = $max = array( + 'time' => NULL, + 'memory' => NULL); + + // Total values are always integers + $total = array( + 'time' => 0, + 'memory' => 0); + + foreach ($tokens as $token) + { + // Get the total time and memory for this benchmark + list($time, $memory) = Profiler::total($token); + + if ($max['time'] === NULL OR $time > $max['time']) + { + // Set the maximum time + $max['time'] = $time; + } + + if ($min['time'] === NULL OR $time < $min['time']) + { + // Set the minimum time + $min['time'] = $time; + } + + // Increase the total time + $total['time'] += $time; + + if ($max['memory'] === NULL OR $memory > $max['memory']) + { + // Set the maximum memory + $max['memory'] = $memory; + } + + if ($min['memory'] === NULL OR $memory < $min['memory']) + { + // Set the minimum memory + $min['memory'] = $memory; + } + + // Increase the total memory + $total['memory'] += $memory; + } + + // Determine the number of tokens + $count = count($tokens); + + // Determine the averages + $average = array( + 'time' => $total['time'] / $count, + 'memory' => $total['memory'] / $count); + + return array( + 'min' => $min, + 'max' => $max, + 'total' => $total, + 'average' => $average); + } + + /** + * Gets the min, max, average and total of profiler groups as an array. + * + * $stats = Profiler::group_stats('test'); + * + * @param mixed $groups single group name string, or array with group names; all groups by default + * @return array min, max, average, total + * @uses Profiler::groups + * @uses Profiler::stats + */ + public static function group_stats($groups = NULL) + { + // Which groups do we need to calculate stats for? + $groups = ($groups === NULL) + ? Profiler::groups() + : array_intersect_key(Profiler::groups(), array_flip( (array) $groups)); + + // All statistics + $stats = array(); + + foreach ($groups as $group => $names) + { + foreach ($names as $name => $tokens) + { + // Store the stats for each subgroup. + // We only need the values for "total". + $_stats = Profiler::stats($tokens); + $stats[$group][$name] = $_stats['total']; + } + } + + // Group stats + $groups = array(); + + foreach ($stats as $group => $names) + { + // Min and max are unknown by default + $groups[$group]['min'] = $groups[$group]['max'] = array( + 'time' => NULL, + 'memory' => NULL); + + // Total values are always integers + $groups[$group]['total'] = array( + 'time' => 0, + 'memory' => 0); + + foreach ($names as $total) + { + if ( ! isset($groups[$group]['min']['time']) OR $groups[$group]['min']['time'] > $total['time']) + { + // Set the minimum time + $groups[$group]['min']['time'] = $total['time']; + } + if ( ! isset($groups[$group]['min']['memory']) OR $groups[$group]['min']['memory'] > $total['memory']) + { + // Set the minimum memory + $groups[$group]['min']['memory'] = $total['memory']; + } + + if ( ! isset($groups[$group]['max']['time']) OR $groups[$group]['max']['time'] < $total['time']) + { + // Set the maximum time + $groups[$group]['max']['time'] = $total['time']; + } + if ( ! isset($groups[$group]['max']['memory']) OR $groups[$group]['max']['memory'] < $total['memory']) + { + // Set the maximum memory + $groups[$group]['max']['memory'] = $total['memory']; + } + + // Increase the total time and memory + $groups[$group]['total']['time'] += $total['time']; + $groups[$group]['total']['memory'] += $total['memory']; + } + + // Determine the number of names (subgroups) + $count = count($names); + + // Determine the averages + $groups[$group]['average']['time'] = $groups[$group]['total']['time'] / $count; + $groups[$group]['average']['memory'] = $groups[$group]['total']['memory'] / $count; + } + + return $groups; + } + + /** + * Gets the total execution time and memory usage of a benchmark as a list. + * + * list($time, $memory) = Profiler::total($token); + * + * @param string $token + * @return array execution time, memory + */ + public static function total($token) + { + // Import the benchmark data + $mark = Profiler::$_marks[$token]; + + if ($mark['stop_time'] === FALSE) + { + // The benchmark has not been stopped yet + $mark['stop_time'] = microtime(TRUE); + $mark['stop_memory'] = memory_get_usage(); + } + + return array + ( + // Total time in seconds + $mark['stop_time'] - $mark['start_time'], + + // Amount of memory in bytes + $mark['stop_memory'] - $mark['start_memory'], + ); + } + + /** + * Gets the total application run time and memory usage. Caches the result + * so that it can be compared between requests. + * + * list($time, $memory) = Profiler::application(); + * + * @return array execution time, memory + * @uses Kohana::cache + */ + public static function application() + { + // Load the stats from cache, which is valid for 1 day + $stats = Kohana::cache('profiler_application_stats', NULL, 3600 * 24); + + if ( ! is_array($stats) OR $stats['count'] > Profiler::$rollover) + { + // Initialize the stats array + $stats = array( + 'min' => array( + 'time' => NULL, + 'memory' => NULL), + 'max' => array( + 'time' => NULL, + 'memory' => NULL), + 'total' => array( + 'time' => NULL, + 'memory' => NULL), + 'count' => 0); + } + + // Get the application run time + $time = microtime(TRUE) - KOHANA_START_TIME; + + // Get the total memory usage + $memory = memory_get_usage() - KOHANA_START_MEMORY; + + // Calculate max time + if ($stats['max']['time'] === NULL OR $time > $stats['max']['time']) + { + $stats['max']['time'] = $time; + } + + // Calculate min time + if ($stats['min']['time'] === NULL OR $time < $stats['min']['time']) + { + $stats['min']['time'] = $time; + } + + // Add to total time + $stats['total']['time'] += $time; + + // Calculate max memory + if ($stats['max']['memory'] === NULL OR $memory > $stats['max']['memory']) + { + $stats['max']['memory'] = $memory; + } + + // Calculate min memory + if ($stats['min']['memory'] === NULL OR $memory < $stats['min']['memory']) + { + $stats['min']['memory'] = $memory; + } + + // Add to total memory + $stats['total']['memory'] += $memory; + + // Another mark has been added to the stats + $stats['count']++; + + // Determine the averages + $stats['average'] = array( + 'time' => $stats['total']['time'] / $stats['count'], + 'memory' => $stats['total']['memory'] / $stats['count']); + + // Cache the new stats + Kohana::cache('profiler_application_stats', $stats); + + // Set the current application execution time and memory + // Do NOT cache these, they are specific to the current request only + $stats['current']['time'] = $time; + $stats['current']['memory'] = $memory; + + // Return the total application run time and memory usage + return $stats; + } + +} // End Profiler diff --git a/system/classes/Kohana/Request.php b/system/classes/Kohana/Request.php new file mode 100644 index 0000000..e5ee71e --- /dev/null +++ b/system/classes/Kohana/Request.php @@ -0,0 +1,1331 @@ +protocol($protocol) + ->query($_GET) + ->post($_POST); + + if (isset($secure)) + { + // Set the request security + $request->secure($secure); + } + + if (isset($method)) + { + // Set the request method + $request->method($method); + } + + if (isset($referrer)) + { + // Set the referrer + $request->referrer($referrer); + } + + if (isset($requested_with)) + { + // Apply the requested with variable + $request->requested_with($requested_with); + } + + if (isset($body)) + { + // Set the request body (probably a PUT type) + $request->body($body); + } + + if (isset($cookies)) + { + $request->cookie($cookies); + } + } + else + { + $request = new Request($uri, $client_params, $allow_external, $injected_routes); + } + + return $request; + } + + /** + * Automatically detects the URI of the main request using PATH_INFO, + * REQUEST_URI, PHP_SELF or REDIRECT_URL. + * + * $uri = Request::detect_uri(); + * + * @return string URI of the main request + * @throws Kohana_Exception + * @since 3.0.8 + */ + public static function detect_uri() + { + if ( ! empty($_SERVER['PATH_INFO'])) + { + // PATH_INFO does not contain the docroot or index + $uri = $_SERVER['PATH_INFO']; + } + else + { + // REQUEST_URI and PHP_SELF include the docroot and index + + if (isset($_SERVER['REQUEST_URI'])) + { + /** + * We use REQUEST_URI as the fallback value. The reason + * for this is we might have a malformed URL such as: + * + * http://localhost/http://example.com/judge.php + * + * which parse_url can't handle. So rather than leave empty + * handed, we'll use this. + */ + $uri = $_SERVER['REQUEST_URI']; + + if ($request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) + { + // Valid URL path found, set it. + $uri = $request_uri; + } + + // Decode the request URI + $uri = rawurldecode($uri); + } + elseif (isset($_SERVER['PHP_SELF'])) + { + $uri = $_SERVER['PHP_SELF']; + } + elseif (isset($_SERVER['REDIRECT_URL'])) + { + $uri = $_SERVER['REDIRECT_URL']; + } + else + { + // If you ever see this error, please report an issue at http://dev.kohanaphp.com/projects/kohana3/issues + // along with any relevant information about your web server setup. Thanks! + throw new Kohana_Exception('Unable to detect the URI using PATH_INFO, REQUEST_URI, PHP_SELF or REDIRECT_URL'); + } + + // Get the path from the base URL, including the index file + $base_url = parse_url(Kohana::$base_url, PHP_URL_PATH); + + if (strpos($uri, $base_url) === 0) + { + // Remove the base URL from the URI + $uri = (string) substr($uri, strlen($base_url)); + } + + if (Kohana::$index_file AND strpos($uri, Kohana::$index_file) === 0) + { + // Remove the index file from the URI + $uri = (string) substr($uri, strlen(Kohana::$index_file)); + } + } + + return $uri; + } + + /** + * Return the currently executing request. This is changed to the current + * request when [Request::execute] is called and restored when the request + * is completed. + * + * $request = Request::current(); + * + * @return Request + * @since 3.0.5 + */ + public static function current() + { + return Request::$current; + } + + /** + * Returns the first request encountered by this framework. This will should + * only be set once during the first [Request::factory] invocation. + * + * // Get the first request + * $request = Request::initial(); + * + * // Test whether the current request is the first request + * if (Request::initial() === Request::current()) + * // Do something useful + * + * @return Request + * @since 3.1.0 + */ + public static function initial() + { + return Request::$initial; + } + + /** + * Returns information about the initial user agent. + * + * @param mixed $value array or string to return: browser, version, robot, mobile, platform + * @return mixed requested information, FALSE if nothing is found + * @uses Request::$user_agent + * @uses Text::user_agent + */ + public static function user_agent($value) + { + return Text::user_agent(Request::$user_agent, $value); + } + + /** + * Returns the accepted content types. If a specific type is defined, + * the quality of that type will be returned. + * + * $types = Request::accept_type(); + * + * [!!] Deprecated in favor of using [HTTP_Header::accepts_at_quality]. + * + * @deprecated since version 3.3.0 + * @param string $type Content MIME type + * @return mixed An array of all types or a specific type as a string + * @uses Request::_parse_accept + */ + public static function accept_type($type = NULL) + { + static $accepts; + + if ($accepts === NULL) + { + // Parse the HTTP_ACCEPT header + $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT'], array('*/*' => 1.0)); + } + + if (isset($type)) + { + // Return the quality setting for this type + return isset($accepts[$type]) ? $accepts[$type] : $accepts['*/*']; + } + + return $accepts; + } + + /** + * Returns the accepted languages. If a specific language is defined, + * the quality of that language will be returned. If the language is not + * accepted, FALSE will be returned. + * + * $langs = Request::accept_lang(); + * + * [!!] Deprecated in favor of using [HTTP_Header::accepts_language_at_quality]. + * + * @deprecated since version 3.3.0 + * @param string $lang Language code + * @return mixed An array of all types or a specific type as a string + * @uses Request::_parse_accept + */ + public static function accept_lang($lang = NULL) + { + static $accepts; + + if ($accepts === NULL) + { + // Parse the HTTP_ACCEPT_LANGUAGE header + $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT_LANGUAGE']); + } + + if (isset($lang)) + { + // Return the quality setting for this lang + return isset($accepts[$lang]) ? $accepts[$lang] : FALSE; + } + + return $accepts; + } + + /** + * Returns the accepted encodings. If a specific encoding is defined, + * the quality of that encoding will be returned. If the encoding is not + * accepted, FALSE will be returned. + * + * $encodings = Request::accept_encoding(); + * + * [!!] Deprecated in favor of using [HTTP_Header::accepts_encoding_at_quality]. + * + * @deprecated since version 3.3.0 + * @param string $type Encoding type + * @return mixed An array of all types or a specific type as a string + * @uses Request::_parse_accept + */ + public static function accept_encoding($type = NULL) + { + static $accepts; + + if ($accepts === NULL) + { + // Parse the HTTP_ACCEPT_LANGUAGE header + $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT_ENCODING']); + } + + if (isset($type)) + { + // Return the quality setting for this type + return isset($accepts[$type]) ? $accepts[$type] : FALSE; + } + + return $accepts; + } + + /** + * Determines if a file larger than the post_max_size has been uploaded. PHP + * does not handle this situation gracefully on its own, so this method + * helps to solve that problem. + * + * @return boolean + * @uses Num::bytes + * @uses Arr::get + */ + public static function post_max_size_exceeded() + { + // Make sure the request method is POST + if (Request::$initial->method() !== HTTP_Request::POST) + return FALSE; + + // Get the post_max_size in bytes + $max_bytes = Num::bytes(ini_get('post_max_size')); + + // Error occurred if method is POST, and content length is too long + return (Arr::get($_SERVER, 'CONTENT_LENGTH') > $max_bytes); + } + + /** + * Process a request to find a matching route + * + * @param object $request Request + * @param array $routes Route + * @return array + */ + public static function process(Request $request, $routes = NULL) + { + // Load routes + $routes = (empty($routes)) ? Route::all() : $routes; + $params = NULL; + + foreach ($routes as $name => $route) + { + // We found something suitable + if ($params = $route->matches($request)) + { + return array( + 'params' => $params, + 'route' => $route, + ); + } + } + + return NULL; + } + + /** + * Parses an accept header and returns an array (type => quality) of the + * accepted types, ordered by quality. + * + * $accept = Request::_parse_accept($header, $defaults); + * + * @param string $header Header to parse + * @param array $accepts Default values + * @return array + */ + protected static function _parse_accept( & $header, array $accepts = NULL) + { + if ( ! empty($header)) + { + // Get all of the types + $types = explode(',', $header); + + foreach ($types as $type) + { + // Split the type into parts + $parts = explode(';', $type); + + // Make the type only the MIME + $type = trim(array_shift($parts)); + + // Default quality is 1.0 + $quality = 1.0; + + foreach ($parts as $part) + { + // Prevent undefined $value notice below + if (strpos($part, '=') === FALSE) + continue; + + // Separate the key and value + list ($key, $value) = explode('=', trim($part)); + + if ($key === 'q') + { + // There is a quality for this type + $quality = (float) trim($value); + } + } + + // Add the accept type and quality + $accepts[$type] = $quality; + } + } + + // Make sure that accepts is an array + $accepts = (array) $accepts; + + // Order by quality + arsort($accepts); + + return $accepts; + } + + /** + * @var string the x-requested-with header which most likely + * will be xmlhttprequest + */ + protected $_requested_with; + + /** + * @var string method: GET, POST, PUT, DELETE, HEAD, etc + */ + protected $_method = 'GET'; + + /** + * @var string protocol: HTTP/1.1, FTP, CLI, etc + */ + protected $_protocol; + + /** + * @var boolean + */ + protected $_secure = FALSE; + + /** + * @var string referring URL + */ + protected $_referrer; + + /** + * @var Route route matched for this request + */ + protected $_route; + + /** + * @var Route array of routes to manually look at instead of the global namespace + */ + protected $_routes; + + /** + * @var Kohana_HTTP_Header headers to sent as part of the request + */ + protected $_header; + + /** + * @var string the body + */ + protected $_body; + + /** + * @var string controller directory + */ + protected $_directory = ''; + + /** + * @var string controller to be executed + */ + protected $_controller; + + /** + * @var string action to be executed in the controller + */ + protected $_action; + + /** + * @var string the URI of the request + */ + protected $_uri; + + /** + * @var boolean external request + */ + protected $_external = FALSE; + + /** + * @var array parameters from the route + */ + protected $_params = array(); + + /** + * @var array query parameters + */ + protected $_get = array(); + + /** + * @var array post parameters + */ + protected $_post = array(); + + /** + * @var array cookies to send with the request + */ + protected $_cookies = array(); + + /** + * @var Kohana_Request_Client + */ + protected $_client; + + /** + * Creates a new request object for the given URI. New requests should be + * created using the [Request::instance] or [Request::factory] methods. + * + * $request = new Request($uri); + * + * If $cache parameter is set, the response for the request will attempt to + * be retrieved from the cache. + * + * @param string $uri URI of the request + * @param array $client_params Array of params to pass to the request client + * @param bool $allow_external Allow external requests? (deprecated in 3.3) + * @param array $injected_routes An array of routes to use, for testing + * @return void + * @throws Request_Exception + * @uses Route::all + * @uses Route::matches + */ + public function __construct($uri, $client_params = array(), $allow_external = TRUE, $injected_routes = array()) + { + $client_params = is_array($client_params) ? $client_params : array(); + + // Initialise the header + $this->_header = new HTTP_Header(array()); + + // Assign injected routes + $this->_routes = $injected_routes; + + // Cleanse query parameters from URI (faster that parse_url()) + $split_uri = explode('?', $uri); + $uri = array_shift($split_uri); + + // Initial request has global $_GET already applied + if (Request::$initial !== NULL) + { + if ($split_uri) + { + parse_str($split_uri[0], $this->_get); + } + } + + // Detect protocol (if present) + // $allow_external = FALSE prevents the default index.php from + // being able to proxy external pages. + if ( ! $allow_external OR strpos($uri, '://') === FALSE) + { + // Remove trailing slashes from the URI + $this->_uri = trim($uri, '/'); + + // Apply the client + $this->_client = new Request_Client_Internal($client_params); + } + else + { + // Create a route + $this->_route = new Route($uri); + + // Store the URI + $this->_uri = $uri; + + // Set the security setting if required + if (strpos($uri, 'https://') === 0) + { + $this->secure(TRUE); + } + + // Set external state + $this->_external = TRUE; + + // Setup the client + $this->_client = Request_Client_External::factory($client_params); + } + } + + /** + * Returns the response as the string representation of a request. + * + * echo $request; + * + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * Sets and gets the uri from the request. + * + * @param string $uri + * @return mixed + */ + public function uri($uri = NULL) + { + if ($uri === NULL) + { + // Act as a getter + return empty($this->_uri) ? '/' : $this->_uri; + } + + // Act as a setter + $this->_uri = $uri; + + return $this; + } + + /** + * Create a URL string from the current request. This is a shortcut for: + * + * echo URL::site($this->request->uri(), $protocol); + * + * @param array $params URI parameters + * @param mixed $protocol protocol string or Request object + * @return string + * @since 3.0.7 + * @uses URL::site + */ + public function url($protocol = NULL) + { + // Create a URI with the current route and convert it to a URL + return URL::site($this->uri(), $protocol); + } + + /** + * Retrieves a value from the route parameters. + * + * $id = $request->param('id'); + * + * @param string $key Key of the value + * @param mixed $default Default value if the key is not set + * @return mixed + */ + public function param($key = NULL, $default = NULL) + { + if ($key === NULL) + { + // Return the full array + return $this->_params; + } + + return isset($this->_params[$key]) ? $this->_params[$key] : $default; + } + + /** + * Sets and gets the referrer from the request. + * + * @param string $referrer + * @return mixed + */ + public function referrer($referrer = NULL) + { + if ($referrer === NULL) + { + // Act as a getter + return $this->_referrer; + } + + // Act as a setter + $this->_referrer = (string) $referrer; + + return $this; + } + + /** + * Sets and gets the route from the request. + * + * @param string $route + * @return mixed + */ + public function route(Route $route = NULL) + { + if ($route === NULL) + { + // Act as a getter + return $this->_route; + } + + // Act as a setter + $this->_route = $route; + + return $this; + } + + /** + * Sets and gets the directory for the controller. + * + * @param string $directory Directory to execute the controller from + * @return mixed + */ + public function directory($directory = NULL) + { + if ($directory === NULL) + { + // Act as a getter + return $this->_directory; + } + + // Act as a setter + $this->_directory = (string) $directory; + + return $this; + } + + /** + * Sets and gets the controller for the matched route. + * + * @param string $controller Controller to execute the action + * @return mixed + */ + public function controller($controller = NULL) + { + if ($controller === NULL) + { + // Act as a getter + return $this->_controller; + } + + // Act as a setter + $this->_controller = (string) $controller; + + return $this; + } + + /** + * Sets and gets the action for the controller. + * + * @param string $action Action to execute the controller from + * @return mixed + */ + public function action($action = NULL) + { + if ($action === NULL) + { + // Act as a getter + return $this->_action; + } + + // Act as a setter + $this->_action = (string) $action; + + return $this; + } + + /** + * Provides access to the [Request_Client]. + * + * @return Request_Client + * @return self + */ + public function client(Request_Client $client = NULL) + { + if ($client === NULL) + return $this->_client; + else + { + $this->_client = $client; + return $this; + } + } + + /** + * Gets and sets the requested with property, which should + * be relative to the x-requested-with pseudo header. + * + * @param string $requested_with Requested with value + * @return mixed + */ + public function requested_with($requested_with = NULL) + { + if ($requested_with === NULL) + { + // Act as a getter + return $this->_requested_with; + } + + // Act as a setter + $this->_requested_with = strtolower($requested_with); + + return $this; + } + + /** + * Processes the request, executing the controller action that handles this + * request, determined by the [Route]. + * + * 1. Before the controller action is called, the [Controller::before] method + * will be called. + * 2. Next the controller action will be called. + * 3. After the controller action is called, the [Controller::after] method + * will be called. + * + * By default, the output from the controller is captured and returned, and + * no headers are sent. + * + * $request->execute(); + * + * @return Response + * @throws Request_Exception + * @throws HTTP_Exception_404 + * @uses [Kohana::$profiling] + * @uses [Profiler] + */ + public function execute() + { + if ( ! $this->_external) + { + $processed = Request::process($this, $this->_routes); + + if ($processed) + { + // Store the matching route + $this->_route = $processed['route']; + $params = $processed['params']; + + // Is this route external? + $this->_external = $this->_route->is_external(); + + if (isset($params['directory'])) + { + // Controllers are in a sub-directory + $this->_directory = $params['directory']; + } + + // Store the controller + $this->_controller = $params['controller']; + + // Store the action + $this->_action = (isset($params['action'])) + ? $params['action'] + : Route::$default_action; + + // These are accessible as public vars and can be overloaded + unset($params['controller'], $params['action'], $params['directory']); + + // Params cannot be changed once matched + $this->_params = $params; + } + } + + if ( ! $this->_route instanceof Route) + { + return HTTP_Exception::factory(404, 'Unable to find a route to match the URI: :uri', array( + ':uri' => $this->_uri, + ))->request($this) + ->get_response(); + } + + if ( ! $this->_client instanceof Request_Client) + { + throw new Request_Exception('Unable to execute :uri without a Kohana_Request_Client', array( + ':uri' => $this->_uri, + )); + } + + return $this->_client->execute($this); + } + + /** + * Returns whether this request is the initial request Kohana received. + * Can be used to test for sub requests. + * + * if ( ! $request->is_initial()) + * // This is a sub request + * + * @return boolean + */ + public function is_initial() + { + return ($this === Request::$initial); + } + + /** + * Readonly access to the [Request::$_external] property. + * + * if ( ! $request->is_external()) + * // This is an internal request + * + * @return boolean + */ + public function is_external() + { + return $this->_external; + } + + /** + * Returns whether this is an ajax request (as used by JS frameworks) + * + * @return boolean + */ + public function is_ajax() + { + return ($this->requested_with() === 'xmlhttprequest'); + } + + /** + * Gets or sets the HTTP method. Usually GET, POST, PUT or DELETE in + * traditional CRUD applications. + * + * @param string $method Method to use for this request + * @return mixed + */ + public function method($method = NULL) + { + if ($method === NULL) + { + // Act as a getter + return $this->_method; + } + + // Act as a setter + $this->_method = strtoupper($method); + + return $this; + } + + /** + * Gets or sets the HTTP protocol. If there is no current protocol set, + * it will use the default set in HTTP::$protocol + * + * @param string $protocol Protocol to set to the request + * @return mixed + */ + public function protocol($protocol = NULL) + { + if ($protocol === NULL) + { + if ($this->_protocol) + return $this->_protocol; + else + return $this->_protocol = HTTP::$protocol; + } + + // Act as a setter + $this->_protocol = strtoupper($protocol); + return $this; + } + + /** + * Getter/Setter to the security settings for this request. This + * method should be treated as immutable. + * + * @param boolean $secure is this request secure? + * @return mixed + */ + public function secure($secure = NULL) + { + if ($secure === NULL) + return $this->_secure; + + // Act as a setter + $this->_secure = (bool) $secure; + return $this; + } + + /** + * Gets or sets HTTP headers oo the request. All headers + * are included immediately after the HTTP protocol definition during + * transmission. This method provides a simple array or key/value + * interface to the headers. + * + * @param mixed $key Key or array of key/value pairs to set + * @param string $value Value to set to the supplied key + * @return mixed + */ + public function headers($key = NULL, $value = NULL) + { + if ($key instanceof HTTP_Header) + { + // Act a setter, replace all headers + $this->_header = $key; + + return $this; + } + + if (is_array($key)) + { + // Act as a setter, replace all headers + $this->_header->exchangeArray($key); + + return $this; + } + + if ($this->_header->count() === 0 AND $this->is_initial()) + { + // Lazy load the request headers + $this->_header = HTTP::request_headers(); + } + + if ($key === NULL) + { + // Act as a getter, return all headers + return $this->_header; + } + elseif ($value === NULL) + { + // Act as a getter, single header + return ($this->_header->offsetExists($key)) ? $this->_header->offsetGet($key) : NULL; + } + + // Act as a setter for a single header + $this->_header[$key] = $value; + + return $this; + } + + /** + * Set and get cookies values for this request. + * + * @param mixed $key Cookie name, or array of cookie values + * @param string $value Value to set to cookie + * @return string + * @return mixed + */ + public function cookie($key = NULL, $value = NULL) + { + if (is_array($key)) + { + // Act as a setter, replace all cookies + $this->_cookies = $key; + return $this; + } + elseif ($key === NULL) + { + // Act as a getter, all cookies + return $this->_cookies; + } + elseif ($value === NULL) + { + // Act as a getting, single cookie + return isset($this->_cookies[$key]) ? $this->_cookies[$key] : NULL; + } + + // Act as a setter for a single cookie + $this->_cookies[$key] = (string) $value; + + return $this; + } + + /** + * Gets or sets the HTTP body of the request. The body is + * included after the header, separated by a single empty new line. + * + * @param string $content Content to set to the object + * @return mixed + */ + public function body($content = NULL) + { + if ($content === NULL) + { + // Act as a getter + return $this->_body; + } + + // Act as a setter + $this->_body = $content; + + return $this; + } + + /** + * Returns the length of the body for use with + * content header + * + * @return integer + */ + public function content_length() + { + return strlen($this->body()); + } + + /** + * Renders the HTTP_Interaction to a string, producing + * + * - Protocol + * - Headers + * - Body + * + * If there are variables set to the `Kohana_Request::$_post` + * they will override any values set to body. + * + * @return string + */ + public function render() + { + if ( ! $post = $this->post()) + { + $body = $this->body(); + } + else + { + $this->headers('content-type', 'application/x-www-form-urlencoded'); + $body = http_build_query($post, NULL, '&'); + } + + // Set the content length + $this->headers('content-length', (string) $this->content_length()); + + // If Kohana expose, set the user-agent + if (Kohana::$expose) + { + $this->headers('user-agent', Kohana::version()); + } + + // Prepare cookies + if ($this->_cookies) + { + $cookie_string = array(); + + // Parse each + foreach ($this->_cookies as $key => $value) + { + $cookie_string[] = $key.'='.$value; + } + + // Create the cookie string + $this->_header['cookie'] = implode('; ', $cookie_string); + } + + $output = $this->method().' '.$this->uri().' '.$this->protocol()."\r\n"; + $output .= (string) $this->_header; + $output .= $body; + + return $output; + } + + /** + * Gets or sets HTTP query string. + * + * @param mixed $key Key or key value pairs to set + * @param string $value Value to set to a key + * @return mixed + * @uses Arr::path + */ + public function query($key = NULL, $value = NULL) + { + if (is_array($key)) + { + // Act as a setter, replace all query strings + $this->_get = $key; + + return $this; + } + + if ($key === NULL) + { + // Act as a getter, all query strings + return $this->_get; + } + elseif ($value === NULL) + { + // Act as a getter, single query string + return Arr::path($this->_get, $key); + } + + // Act as a setter, single query string + $this->_get[$key] = $value; + + return $this; + } + + /** + * Gets or sets HTTP POST parameters to the request. + * + * @param mixed $key Key or key value pairs to set + * @param string $value Value to set to a key + * @return mixed + * @uses Arr::path + */ + public function post($key = NULL, $value = NULL) + { + if (is_array($key)) + { + // Act as a setter, replace all fields + $this->_post = $key; + + return $this; + } + + if ($key === NULL) + { + // Act as a getter, all fields + return $this->_post; + } + elseif ($value === NULL) + { + // Act as a getter, single field + return Arr::path($this->_post, $key); + } + + // Act as a setter, single field + $this->_post[$key] = $value; + + return $this; + } + +} // End Request diff --git a/system/classes/Kohana/Request/Client.php b/system/classes/Kohana/Request/Client.php new file mode 100644 index 0000000..d23b15f --- /dev/null +++ b/system/classes/Kohana/Request/Client.php @@ -0,0 +1,424 @@ + 'Request_Client::on_header_location' + ); + + /** + * @var int Maximum number of requests that header callbacks can trigger before the request is aborted + */ + protected $_max_callback_depth = 5; + + /** + * @var int Tracks the callback depth of the currently executing request + */ + protected $_callback_depth = 1; + + /** + * @var array Arbitrary parameters that are shared with header callbacks through their Request_Client object + */ + protected $_callback_params = array(); + + /** + * Creates a new `Request_Client` object, + * allows for dependency injection. + * + * @param array $params Params + */ + public function __construct(array $params = array()) + { + foreach ($params as $key => $value) + { + if (method_exists($this, $key)) + { + $this->$key($value); + } + } + } + + /** + * Processes the request, executing the controller action that handles this + * request, determined by the [Route]. + * + * 1. Before the controller action is called, the [Controller::before] method + * will be called. + * 2. Next the controller action will be called. + * 3. After the controller action is called, the [Controller::after] method + * will be called. + * + * By default, the output from the controller is captured and returned, and + * no headers are sent. + * + * $request->execute(); + * + * @param Request $request + * @param Response $response + * @return Response + * @throws Kohana_Exception + * @uses [Kohana::$profiling] + * @uses [Profiler] + */ + public function execute(Request $request) + { + // Prevent too much recursion of header callback requests + if ($this->callback_depth() > $this->max_callback_depth()) + throw new Request_Client_Recursion_Exception( + "Could not execute request to :uri - too many recursions after :depth requests", + array( + ':uri' => $request->uri(), + ':depth' => $this->callback_depth() - 1, + )); + + // Execute the request + $orig_response = $response = Response::factory(); + + if (($cache = $this->cache()) instanceof HTTP_Cache) + return $cache->execute($this, $request, $response); + + $response = $this->execute_request($request, $response); + + // Execute response callbacks + foreach ($this->header_callbacks() as $header => $callback) + { + if ($response->headers($header)) + { + $cb_result = call_user_func($callback, $request, $response, $this); + + if ($cb_result instanceof Request) + { + // If the callback returns a request, automatically assign client params + $this->assign_client_properties($cb_result->client()); + $cb_result->client()->callback_depth($this->callback_depth() + 1); + + // Execute the request + $response = $cb_result->execute(); + } + elseif ($cb_result instanceof Response) + { + // Assign the returned response + $response = $cb_result; + } + + // If the callback has created a new response, do not process any further + if ($response !== $orig_response) + break; + } + } + + return $response; + } + + /** + * Processes the request passed to it and returns the response from + * the URI resource identified. + * + * This method must be implemented by all clients. + * + * @param Request $request request to execute by client + * @param Response $response + * @return Response + * @since 3.2.0 + */ + abstract public function execute_request(Request $request, Response $response); + + /** + * Getter and setter for the internal caching engine, + * used to cache responses if available and valid. + * + * @param HTTP_Cache $cache engine to use for caching + * @return HTTP_Cache + * @return Request_Client + */ + public function cache(HTTP_Cache $cache = NULL) + { + if ($cache === NULL) + return $this->_cache; + + $this->_cache = $cache; + return $this; + } + + /** + * Getter and setter for the follow redirects + * setting. + * + * @param bool $follow Boolean indicating if redirects should be followed + * @return bool + * @return Request_Client + */ + public function follow($follow = NULL) + { + if ($follow === NULL) + return $this->_follow; + + $this->_follow = $follow; + + return $this; + } + + /** + * Getter and setter for the follow redirects + * headers array. + * + * @param array $follow_headers Array of headers to be re-used when following a Location header + * @return array + * @return Request_Client + */ + public function follow_headers($follow_headers = NULL) + { + if ($follow_headers === NULL) + return $this->_follow_headers; + + $this->_follow_headers = $follow_headers; + + return $this; + } + + /** + * Getter and setter for the strict redirects setting + * + * [!!] HTTP/1.1 specifies that a 302 redirect should be followed using the + * original request method. However, the vast majority of clients and servers + * get this wrong, with 302 widely used for 'POST - 302 redirect - GET' patterns. + * By default, Kohana's client is fully compliant with the HTTP spec. Some + * non-compliant third party sites may require that strict_redirect is set + * FALSE to force the client to switch to GET following a 302 response. + * + * @param bool $strict_redirect Boolean indicating if 302 redirects should be followed with the original method + * @return Request_Client + */ + public function strict_redirect($strict_redirect = NULL) + { + if ($strict_redirect === NULL) + return $this->_strict_redirect; + + $this->_strict_redirect = $strict_redirect; + + return $this; + } + + /** + * Getter and setter for the header callbacks array. + * + * Accepts an array with HTTP response headers as keys and a PHP callback + * function as values. These callbacks will be triggered if a response contains + * the given header and can either issue a subsequent request or manipulate + * the response as required. + * + * By default, the [Request_Client::on_header_location] callback is assigned + * to the Location header to support automatic redirect following. + * + * $client->header_callbacks(array( + * 'Location' => 'Request_Client::on_header_location', + * 'WWW-Authenticate' => function($request, $response, $client) {return $new_response;}, + * ); + * + * @param array $header_callbacks Array of callbacks to trigger on presence of given headers + * @return Request_Client + */ + public function header_callbacks($header_callbacks = NULL) + { + if ($header_callbacks === NULL) + return $this->_header_callbacks; + + $this->_header_callbacks = $header_callbacks; + + return $this; + } + + /** + * Getter and setter for the maximum callback depth property. + * + * This protects the main execution from recursive callback execution (eg + * following infinite redirects, conflicts between callbacks causing loops + * etc). Requests will only be allowed to nest to the level set by this + * param before execution is aborted with a Request_Client_Recursion_Exception. + * + * @param int $depth Maximum number of callback requests to execute before aborting + * @return Request_Client|int + */ + public function max_callback_depth($depth = NULL) + { + if ($depth === NULL) + return $this->_max_callback_depth; + + $this->_max_callback_depth = $depth; + + return $this; + } + + /** + * Getter/Setter for the callback depth property, which is used to track + * how many recursions have been executed within the current request execution. + * + * @param int $depth Current recursion depth + * @return Request_Client|int + */ + public function callback_depth($depth = NULL) + { + if ($depth === NULL) + return $this->_callback_depth; + + $this->_callback_depth = $depth; + + return $this; + } + + /** + * Getter/Setter for the callback_params array, which allows additional + * application-specific parameters to be shared with callbacks. + * + * As with other Kohana setter/getters, usage is: + * + * // Set full array + * $client->callback_params(array('foo'=>'bar')); + * + * // Set single key + * $client->callback_params('foo','bar'); + * + * // Get full array + * $params = $client->callback_params(); + * + * // Get single key + * $foo = $client->callback_params('foo'); + * + * @param string|array $param + * @param mixed $value + * @return Request_Client|mixed + */ + public function callback_params($param = NULL, $value = NULL) + { + // Getter for full array + if ($param === NULL) + return $this->_callback_params; + + // Setter for full array + if (is_array($param)) + { + $this->_callback_params = $param; + return $this; + } + // Getter for single value + elseif ($value === NULL) + { + return Arr::get($this->_callback_params, $param); + } + // Setter for single value + else + { + $this->_callback_params[$param] = $value; + return $this; + } + + } + + /** + * Assigns the properties of the current Request_Client to another + * Request_Client instance - used when setting up a subsequent request. + * + * @param Request_Client $client + */ + public function assign_client_properties(Request_Client $client) + { + $client->cache($this->cache()); + $client->follow($this->follow()); + $client->follow_headers($this->follow_headers()); + $client->header_callbacks($this->header_callbacks()); + $client->max_callback_depth($this->max_callback_depth()); + $client->callback_params($this->callback_params()); + } + + /** + * The default handler for following redirects, triggered by the presence of + * a Location header in the response. + * + * The client's follow property must be set TRUE and the HTTP response status + * one of 201, 301, 302, 303 or 307 for the redirect to be followed. + * + * @param Request $request + * @param Response $response + * @param Request_Client $client + */ + public static function on_header_location(Request $request, Response $response, Request_Client $client) + { + // Do we need to follow a Location header ? + if ($client->follow() AND in_array($response->status(), array(201, 301, 302, 303, 307))) + { + // Figure out which method to use for the follow request + switch ($response->status()) + { + default: + case 301: + case 307: + $follow_method = $request->method(); + break; + case 201: + case 303: + $follow_method = Request::GET; + break; + case 302: + // Cater for sites with broken HTTP redirect implementations + if ($client->strict_redirect()) + { + $follow_method = $request->method(); + } + else + { + $follow_method = Request::GET; + } + break; + } + + // Prepare the additional request + $follow_request = Request::factory($response->headers('Location')) + ->method($follow_method) + ->headers(Arr::extract($request->headers(), $client->follow_headers())); + + if ($follow_method !== Request::GET) + { + $follow_request->body($request->body()); + } + + return $follow_request; + } + + return NULL; + } + +} \ No newline at end of file diff --git a/system/classes/Kohana/Request/Client/Curl.php b/system/classes/Kohana/Request/Client/Curl.php new file mode 100644 index 0000000..c5c85d3 --- /dev/null +++ b/system/classes/Kohana/Request/Client/Curl.php @@ -0,0 +1,135 @@ +_set_curl_request_method($request, $options); + + // Set the request body. This is perfectly legal in CURL even + // if using a request other than POST. PUT does support this method + // and DOES NOT require writing data to disk before putting it, if + // reading the PHP docs you may have got that impression. SdF + $options[CURLOPT_POSTFIELDS] = $request->body(); + + // Process headers + if ($headers = $request->headers()) + { + $http_headers = array(); + + foreach ($headers as $key => $value) + { + $http_headers[] = $key.': '.$value; + } + + $options[CURLOPT_HTTPHEADER] = $http_headers; + } + + // Process cookies + if ($cookies = $request->cookie()) + { + $options[CURLOPT_COOKIE] = http_build_query($cookies, NULL, '; '); + } + + // Get any exisiting response headers + $response_header = $response->headers(); + + // Implement the standard parsing parameters + $options[CURLOPT_HEADERFUNCTION] = array($response_header, 'parse_header_string'); + $this->_options[CURLOPT_RETURNTRANSFER] = TRUE; + $this->_options[CURLOPT_HEADER] = FALSE; + + // Apply any additional options set to + $options += $this->_options; + + $uri = $request->uri(); + + if ($query = $request->query()) + { + $uri .= '?'.http_build_query($query, NULL, '&'); + } + + // Open a new remote connection + $curl = curl_init($uri); + + // Set connection options + if ( ! curl_setopt_array($curl, $options)) + { + throw new Request_Exception('Failed to set CURL options, check CURL documentation: :url', + array(':url' => 'http://php.net/curl_setopt_array')); + } + + // Get the response body + $body = curl_exec($curl); + + // Get the response information + $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + + if ($body === FALSE) + { + $error = curl_error($curl); + } + + // Close the connection + curl_close($curl); + + if (isset($error)) + { + throw new Request_Exception('Error fetching remote :url [ status :code ] :error', + array(':url' => $request->url(), ':code' => $code, ':error' => $error)); + } + + $response->status($code) + ->body($body); + + return $response; + } + + /** + * Sets the appropriate curl request options. Uses the responding options + * for POST and PUT, uses CURLOPT_CUSTOMREQUEST otherwise + * @param Request $request + * @param array $options + * @return array + */ + public function _set_curl_request_method(Request $request, array $options) + { + switch ($request->method()) { + case Request::POST: + $options[CURLOPT_POST] = TRUE; + break; + case Request::PUT: + $options[CURLOPT_PUT] = TRUE; + break; + default: + $options[CURLOPT_CUSTOMREQUEST] = $request->method(); + break; + } + return $options; + } + +} // End Kohana_Request_Client_Curl \ No newline at end of file diff --git a/system/classes/Kohana/Request/Client/External.php b/system/classes/Kohana/Request/Client/External.php new file mode 100644 index 0000000..7bd754f --- /dev/null +++ b/system/classes/Kohana/Request/Client/External.php @@ -0,0 +1,207 @@ +client(Request_Client_External::factory('Request_Client_HTTP)); + * + * @package Kohana + * @category Base + * @author Kohana Team + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + * @uses [PECL HTTP](http://php.net/manual/en/book.http.php) + */ +abstract class Kohana_Request_Client_External extends Request_Client { + + /** + * Use: + * - Request_Client_Curl (default) + * - Request_Client_HTTP + * - Request_Client_Stream + * + * @var string defines the external client to use by default + */ + public static $client = 'Request_Client_Curl'; + + /** + * Factory method to create a new Request_Client_External object based on + * the client name passed, or defaulting to Request_Client_External::$client + * by default. + * + * Request_Client_External::$client can be set in the application bootstrap. + * + * @param array $params parameters to pass to the client + * @param string $client external client to use + * @return Request_Client_External + * @throws Request_Exception + */ + public static function factory(array $params = array(), $client = NULL) + { + if ($client === NULL) + { + $client = Request_Client_External::$client; + } + + $client = new $client($params); + + if ( ! $client instanceof Request_Client_External) + { + throw new Request_Exception('Selected client is not a Request_Client_External object.'); + } + + return $client; + } + + /** + * @var array curl options + * @link http://www.php.net/manual/function.curl-setopt + * @link http://www.php.net/manual/http.request.options + */ + protected $_options = array(); + + /** + * Processes the request, executing the controller action that handles this + * request, determined by the [Route]. + * + * 1. Before the controller action is called, the [Controller::before] method + * will be called. + * 2. Next the controller action will be called. + * 3. After the controller action is called, the [Controller::after] method + * will be called. + * + * By default, the output from the controller is captured and returned, and + * no headers are sent. + * + * $request->execute(); + * + * @param Request $request A request object + * @param Response $response A response object + * @return Response + * @throws Kohana_Exception + * @uses [Kohana::$profiling] + * @uses [Profiler] + */ + public function execute_request(Request $request, Response $response) + { + if (Kohana::$profiling) + { + // Set the benchmark name + $benchmark = '"'.$request->uri().'"'; + + if ($request !== Request::$initial AND Request::$current) + { + // Add the parent request uri + $benchmark .= ' « "'.Request::$current->uri().'"'; + } + + // Start benchmarking + $benchmark = Profiler::start('Requests', $benchmark); + } + + // Store the current active request and replace current with new request + $previous = Request::$current; + Request::$current = $request; + + // Resolve the POST fields + if ($post = $request->post()) + { + $request->body(http_build_query($post, NULL, '&')) + ->headers('content-type', 'application/x-www-form-urlencoded'); + } + + // If Kohana expose, set the user-agent + if (Kohana::$expose) + { + $request->headers('user-agent', Kohana::version()); + } + + try + { + $response = $this->_send_message($request, $response); + } + catch (Exception $e) + { + // Restore the previous request + Request::$current = $previous; + + if (isset($benchmark)) + { + // Delete the benchmark, it is invalid + Profiler::delete($benchmark); + } + + // Re-throw the exception + throw $e; + } + + // Restore the previous request + Request::$current = $previous; + + if (isset($benchmark)) + { + // Stop the benchmark + Profiler::stop($benchmark); + } + + // Return the response + return $response; + } + + /** + * Set and get options for this request. + * + * @param mixed $key Option name, or array of options + * @param mixed $value Option value + * @return mixed + * @return Request_Client_External + */ + public function options($key = NULL, $value = NULL) + { + if ($key === NULL) + return $this->_options; + + if (is_array($key)) + { + $this->_options = $key; + } + elseif ($value === NULL) + { + return Arr::get($this->_options, $key); + } + else + { + $this->_options[$key] = $value; + } + + return $this; + } + + /** + * Sends the HTTP message [Request] to a remote server and processes + * the response. + * + * @param Request $request Request to send + * @param Response $response Response to send + * @return Response + */ + abstract protected function _send_message(Request $request, Response $response); + +} // End Kohana_Request_Client_External \ No newline at end of file diff --git a/system/classes/Kohana/Request/Client/HTTP.php b/system/classes/Kohana/Request/Client/HTTP.php new file mode 100644 index 0000000..c74f5c1 --- /dev/null +++ b/system/classes/Kohana/Request/Client/HTTP.php @@ -0,0 +1,121 @@ + HTTPRequest::METH_GET, + HTTP_Request::HEAD => HTTPRequest::METH_HEAD, + HTTP_Request::POST => HTTPRequest::METH_POST, + HTTP_Request::PUT => HTTPRequest::METH_PUT, + HTTP_Request::DELETE => HTTPRequest::METH_DELETE, + HTTP_Request::OPTIONS => HTTPRequest::METH_OPTIONS, + HTTP_Request::TRACE => HTTPRequest::METH_TRACE, + HTTP_Request::CONNECT => HTTPRequest::METH_CONNECT, + ); + + // Create an http request object + $http_request = new HTTPRequest($request->uri(), $http_method_mapping[$request->method()]); + + if ($this->_options) + { + // Set custom options + $http_request->setOptions($this->_options); + } + + // Set headers + $http_request->setHeaders($request->headers()->getArrayCopy()); + + // Set cookies + $http_request->setCookies($request->cookie()); + + // Set query data (?foo=bar&bar=foo) + $http_request->setQueryData($request->query()); + + // Set the body + if ($request->method() == HTTP_Request::PUT) + { + $http_request->addPutData($request->body()); + } + else + { + $http_request->setBody($request->body()); + } + + try + { + $http_request->send(); + } + catch (HTTPRequestException $e) + { + throw new Request_Exception($e->getMessage()); + } + catch (HTTPMalformedHeaderException $e) + { + throw new Request_Exception($e->getMessage()); + } + catch (HTTPEncodingException $e) + { + throw new Request_Exception($e->getMessage()); + } + + // Build the response + $response->status($http_request->getResponseCode()) + ->headers($http_request->getResponseHeader()) + ->cookie($http_request->getResponseCookies()) + ->body($http_request->getResponseBody()); + + return $response; + } + +} // End Kohana_Request_Client_HTTP diff --git a/system/classes/Kohana/Request/Client/Internal.php b/system/classes/Kohana/Request/Client/Internal.php new file mode 100644 index 0000000..6afe224 --- /dev/null +++ b/system/classes/Kohana/Request/Client/Internal.php @@ -0,0 +1,128 @@ +execute(); + * + * @param Request $request + * @return Response + * @throws Kohana_Exception + * @uses [Kohana::$profiling] + * @uses [Profiler] + */ + public function execute_request(Request $request, Response $response) + { + // Create the class prefix + $prefix = 'Controller_'; + + // Directory + $directory = $request->directory(); + + // Controller + $controller = $request->controller(); + + if ($directory) + { + // Add the directory name to the class prefix + $prefix .= str_replace(array('\\', '/'), '_', trim($directory, '/')).'_'; + } + + if (Kohana::$profiling) + { + // Set the benchmark name + $benchmark = '"'.$request->uri().'"'; + + if ($request !== Request::$initial AND Request::$current) + { + // Add the parent request uri + $benchmark .= ' « "'.Request::$current->uri().'"'; + } + + // Start benchmarking + $benchmark = Profiler::start('Requests', $benchmark); + } + + // Store the currently active request + $previous = Request::$current; + + // Change the current request to this request + Request::$current = $request; + + // Is this the initial request + $initial_request = ($request === Request::$initial); + + try + { + if ( ! class_exists($prefix.$controller)) + { + throw HTTP_Exception::factory(404, + 'The requested URL :uri was not found on this server.', + array(':uri' => $request->uri()) + )->request($request); + } + + // Load the controller using reflection + $class = new ReflectionClass($prefix.$controller); + + if ($class->isAbstract()) + { + throw new Kohana_Exception( + 'Cannot create instances of abstract :controller', + array(':controller' => $prefix.$controller) + ); + } + + // Create a new instance of the controller + $controller = $class->newInstance($request, $response); + + // Run the controller's execute() method + $response = $class->getMethod('execute')->invoke($controller); + + if ( ! $response instanceof Response) + { + // Controller failed to return a Response. + throw new Kohana_Exception('Controller failed to return a Response'); + } + } + catch (HTTP_Exception $e) + { + // Get the response via the Exception + $response = $e->get_response(); + } + catch (Exception $e) + { + // Generate an appropriate Response object + $response = Kohana_Exception::_handler($e); + } + + // Restore the previous request + Request::$current = $previous; + + if (isset($benchmark)) + { + // Stop the benchmark + Profiler::stop($benchmark); + } + + // Return the response + return $response; + } +} // End Kohana_Request_Client_Internal diff --git a/system/classes/Kohana/Request/Client/Recursion/Exception.php b/system/classes/Kohana/Request/Client/Recursion/Exception.php new file mode 100644 index 0000000..3029a8f --- /dev/null +++ b/system/classes/Kohana/Request/Client/Recursion/Exception.php @@ -0,0 +1,10 @@ +method() === HTTP_Request::GET) ? 'r' : 'r+'; + + // Process cookies + if ($cookies = $request->cookie()) + { + $request->headers('cookie', http_build_query($cookies, NULL, '; ')); + } + + // Get the message body + $body = $request->body(); + + if (is_resource($body)) + { + $body = stream_get_contents($body); + } + + // Set the content length + $request->headers('content-length', (string) strlen($body)); + + list($protocol) = explode('/', $request->protocol()); + + // Create the context + $options = array( + strtolower($protocol) => array( + 'method' => $request->method(), + 'header' => (string) $request->headers(), + 'content' => $body + ) + ); + + // Create the context stream + $context = stream_context_create($options); + + stream_context_set_option($context, $this->_options); + + $uri = $request->uri(); + + if ($query = $request->query()) + { + $uri .= '?'.http_build_query($query, NULL, '&'); + } + + $stream = fopen($uri, $mode, FALSE, $context); + + $meta_data = stream_get_meta_data($stream); + + // Get the HTTP response code + $http_response = array_shift($meta_data['wrapper_data']); + + if (preg_match_all('/(\w+\/\d\.\d) (\d{3})/', $http_response, $matches) !== FALSE) + { + $protocol = $matches[1][0]; + $status = (int) $matches[2][0]; + } + else + { + $protocol = NULL; + $status = NULL; + } + + // Get any exisiting response headers + $response_header = $response->headers(); + + // Process headers + array_map(array($response_header, 'parse_header_string'), array(), $meta_data['wrapper_data']); + + $response->status($status) + ->protocol($protocol) + ->body(stream_get_contents($stream)); + + // Close the stream after use + fclose($stream); + + return $response; + } + +} // End Kohana_Request_Client_Stream \ No newline at end of file diff --git a/system/classes/Kohana/Request/Exception.php b/system/classes/Kohana/Request/Exception.php new file mode 100644 index 0000000..7b7636b --- /dev/null +++ b/system/classes/Kohana/Request/Exception.php @@ -0,0 +1,9 @@ + 200)); + * + * @param array $config Setup the response object + * @return Response + */ + public static function factory(array $config = array()) + { + return new Response($config); + } + + // HTTP status codes and messages + public static $messages = array( + // Informational 1xx + 100 => 'Continue', + 101 => 'Switching Protocols', + + // Success 2xx + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + // Redirection 3xx + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', // 1.1 + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + // 306 is deprecated but reserved + 307 => 'Temporary Redirect', + + // Client Error 4xx + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + + // Server Error 5xx + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 509 => 'Bandwidth Limit Exceeded' + ); + + /** + * @var integer The response http status + */ + protected $_status = 200; + + /** + * @var HTTP_Header Headers returned in the response + */ + protected $_header; + + /** + * @var string The response body + */ + protected $_body = ''; + + /** + * @var array Cookies to be returned in the response + */ + protected $_cookies = array(); + + /** + * @var string The response protocol + */ + protected $_protocol; + + /** + * Sets up the response object + * + * @param array $config Setup the response object + * @return void + */ + public function __construct(array $config = array()) + { + $this->_header = new HTTP_Header; + + foreach ($config as $key => $value) + { + if (property_exists($this, $key)) + { + if ($key == '_header') + { + $this->headers($value); + } + else + { + $this->$key = $value; + } + } + } + } + + /** + * Outputs the body when cast to string + * + * @return string + */ + public function __toString() + { + return $this->_body; + } + + /** + * Gets or sets the body of the response + * + * @return mixed + */ + public function body($content = NULL) + { + if ($content === NULL) + return $this->_body; + + $this->_body = (string) $content; + return $this; + } + + /** + * Gets or sets the HTTP protocol. The standard protocol to use + * is `HTTP/1.1`. + * + * @param string $protocol Protocol to set to the request/response + * @return mixed + */ + public function protocol($protocol = NULL) + { + if ($protocol) + { + $this->_protocol = strtoupper($protocol); + return $this; + } + + if ($this->_protocol === NULL) + { + $this->_protocol = HTTP::$protocol; + } + + return $this->_protocol; + } + + /** + * Sets or gets the HTTP status from this response. + * + * // Set the HTTP status to 404 Not Found + * $response = Response::factory() + * ->status(404); + * + * // Get the current status + * $status = $response->status(); + * + * @param integer $status Status to set to this response + * @return mixed + */ + public function status($status = NULL) + { + if ($status === NULL) + { + return $this->_status; + } + elseif (array_key_exists($status, Response::$messages)) + { + $this->_status = (int) $status; + return $this; + } + else + { + throw new Kohana_Exception(__METHOD__.' unknown status value : :value', array(':value' => $status)); + } + } + + /** + * Gets and sets headers to the [Response], allowing chaining + * of response methods. If chaining isn't required, direct + * access to the property should be used instead. + * + * // Get a header + * $accept = $response->headers('Content-Type'); + * + * // Set a header + * $response->headers('Content-Type', 'text/html'); + * + * // Get all headers + * $headers = $response->headers(); + * + * // Set multiple headers + * $response->headers(array('Content-Type' => 'text/html', 'Cache-Control' => 'no-cache')); + * + * @param mixed $key + * @param string $value + * @return mixed + */ + public function headers($key = NULL, $value = NULL) + { + if ($key === NULL) + { + return $this->_header; + } + elseif (is_array($key)) + { + $this->_header->exchangeArray($key); + return $this; + } + elseif ($value === NULL) + { + return Arr::get($this->_header, $key); + } + else + { + $this->_header[$key] = $value; + return $this; + } + } + + /** + * Returns the length of the body for use with + * content header + * + * @return integer + */ + public function content_length() + { + return strlen($this->body()); + } + + /** + * Set and get cookies values for this response. + * + * // Get the cookies set to the response + * $cookies = $response->cookie(); + * + * // Set a cookie to the response + * $response->cookie('session', array( + * 'value' => $value, + * 'expiration' => 12352234 + * )); + * + * @param mixed $key cookie name, or array of cookie values + * @param string $value value to set to cookie + * @return string + * @return void + * @return [Response] + */ + public function cookie($key = NULL, $value = NULL) + { + // Handle the get cookie calls + if ($key === NULL) + return $this->_cookies; + elseif ( ! is_array($key) AND ! $value) + return Arr::get($this->_cookies, $key); + + // Handle the set cookie calls + if (is_array($key)) + { + reset($key); + while (list($_key, $_value) = each($key)) + { + $this->cookie($_key, $_value); + } + } + else + { + if ( ! is_array($value)) + { + $value = array( + 'value' => $value, + 'expiration' => Cookie::$expiration + ); + } + elseif ( ! isset($value['expiration'])) + { + $value['expiration'] = Cookie::$expiration; + } + + $this->_cookies[$key] = $value; + } + + return $this; + } + + /** + * Deletes a cookie set to the response + * + * @param string $name + * @return Response + */ + public function delete_cookie($name) + { + unset($this->_cookies[$name]); + return $this; + } + + /** + * Deletes all cookies from this response + * + * @return Response + */ + public function delete_cookies() + { + $this->_cookies = array(); + return $this; + } + + /** + * Sends the response status and all set headers. + * + * @param boolean $replace replace existing headers + * @param callback $callback function to handle header output + * @return mixed + */ + public function send_headers($replace = FALSE, $callback = NULL) + { + return $this->_header->send_headers($this, $replace, $callback); + } + + /** + * Send file download as the response. All execution will be halted when + * this method is called! Use TRUE for the filename to send the current + * response as the file content. The third parameter allows the following + * options to be set: + * + * Type | Option | Description | Default Value + * ----------|-----------|------------------------------------|-------------- + * `boolean` | inline | Display inline instead of download | `FALSE` + * `string` | mime_type | Manual mime type | Automatic + * `boolean` | delete | Delete the file after sending | `FALSE` + * + * Download a file that already exists: + * + * $request->send_file('media/packages/kohana.zip'); + * + * Download generated content as a file: + * + * $request->response($content); + * $request->send_file(TRUE, $filename); + * + * [!!] No further processing can be done after this method is called! + * + * @param string $filename filename with path, or TRUE for the current response + * @param string $download downloaded file name + * @param array $options additional options + * @return void + * @throws Kohana_Exception + * @uses File::mime_by_ext + * @uses File::mime + * @uses Request::send_headers + */ + public function send_file($filename, $download = NULL, array $options = NULL) + { + if ( ! empty($options['mime_type'])) + { + // The mime-type has been manually set + $mime = $options['mime_type']; + } + + if ($filename === TRUE) + { + if (empty($download)) + { + throw new Kohana_Exception('Download name must be provided for streaming files'); + } + + // Temporary files will automatically be deleted + $options['delete'] = FALSE; + + if ( ! isset($mime)) + { + // Guess the mime using the file extension + $mime = File::mime_by_ext(strtolower(pathinfo($download, PATHINFO_EXTENSION))); + } + + // Force the data to be rendered if + $file_data = (string) $this->_body; + + // Get the content size + $size = strlen($file_data); + + // Create a temporary file to hold the current response + $file = tmpfile(); + + // Write the current response into the file + fwrite($file, $file_data); + + // File data is no longer needed + unset($file_data); + } + else + { + // Get the complete file path + $filename = realpath($filename); + + if (empty($download)) + { + // Use the file name as the download file name + $download = pathinfo($filename, PATHINFO_BASENAME); + } + + // Get the file size + $size = filesize($filename); + + if ( ! isset($mime)) + { + // Get the mime type from the extension of the download file + $mime = File::mime_by_ext(pathinfo($download, PATHINFO_EXTENSION)); + } + + // Open the file for reading + $file = fopen($filename, 'rb'); + } + + if ( ! is_resource($file)) + { + throw new Kohana_Exception('Could not read file to send: :file', array( + ':file' => $download, + )); + } + + // Inline or download? + $disposition = empty($options['inline']) ? 'attachment' : 'inline'; + + // Calculate byte range to download. + list($start, $end) = $this->_calculate_byte_range($size); + + if ( ! empty($options['resumable'])) + { + if ($start > 0 OR $end < ($size - 1)) + { + // Partial Content + $this->_status = 206; + } + + // Range of bytes being sent + $this->_header['content-range'] = 'bytes '.$start.'-'.$end.'/'.$size; + $this->_header['accept-ranges'] = 'bytes'; + } + + // Set the headers for a download + $this->_header['content-disposition'] = $disposition.'; filename="'.$download.'"'; + $this->_header['content-type'] = $mime; + $this->_header['content-length'] = (string) (($end - $start) + 1); + + if (Request::user_agent('browser') === 'Internet Explorer') + { + // Naturally, IE does not act like a real browser... + if (Request::$initial->secure()) + { + // http://support.microsoft.com/kb/316431 + $this->_header['pragma'] = $this->_header['cache-control'] = 'public'; + } + + if (version_compare(Request::user_agent('version'), '8.0', '>=')) + { + // http://ajaxian.com/archives/ie-8-security + $this->_header['x-content-type-options'] = 'nosniff'; + } + } + + // Send all headers now + $this->send_headers(); + + while (ob_get_level()) + { + // Flush all output buffers + ob_end_flush(); + } + + // Manually stop execution + ignore_user_abort(TRUE); + + if ( ! Kohana::$safe_mode) + { + // Keep the script running forever + set_time_limit(0); + } + + // Send data in 16kb blocks + $block = 1024 * 16; + + fseek($file, $start); + + while ( ! feof($file) AND ($pos = ftell($file)) <= $end) + { + if (connection_aborted()) + break; + + if ($pos + $block > $end) + { + // Don't read past the buffer. + $block = $end - $pos + 1; + } + + // Output a block of the file + echo fread($file, $block); + + // Send the data now + flush(); + } + + // Close the file + fclose($file); + + if ( ! empty($options['delete'])) + { + try + { + // Attempt to remove the file + unlink($filename); + } + catch (Exception $e) + { + // Create a text version of the exception + $error = Kohana_Exception::text($e); + + if (is_object(Kohana::$log)) + { + // Add this exception to the log + Kohana::$log->add(Log::ERROR, $error); + + // Make sure the logs are written + Kohana::$log->write(); + } + + // Do NOT display the exception, it will corrupt the output! + } + } + + // Stop execution + exit; + } + + /** + * Renders the HTTP_Interaction to a string, producing + * + * - Protocol + * - Headers + * - Body + * + * @return string + */ + public function render() + { + if ( ! $this->_header->offsetExists('content-type')) + { + // Add the default Content-Type header if required + $this->_header['content-type'] = Kohana::$content_type.'; charset='.Kohana::$charset; + } + + // Set the content length + $this->headers('content-length', (string) $this->content_length()); + + // If Kohana expose, set the user-agent + if (Kohana::$expose) + { + $this->headers('user-agent', Kohana::version()); + } + + // Prepare cookies + if ($this->_cookies) + { + if (extension_loaded('http')) + { + $this->_header['set-cookie'] = http_build_cookie($this->_cookies); + } + else + { + $cookies = array(); + + // Parse each + foreach ($this->_cookies as $key => $value) + { + $string = $key.'='.$value['value'].'; expires='.date('l, d M Y H:i:s T', $value['expiration']); + $cookies[] = $string; + } + + // Create the cookie string + $this->_header['set-cookie'] = $cookies; + } + } + + $output = $this->_protocol.' '.$this->_status.' '.Response::$messages[$this->_status]."\r\n"; + $output .= (string) $this->_header; + $output .= $this->_body; + + return $output; + } + + /** + * Generate ETag + * Generates an ETag from the response ready to be returned + * + * @throws Request_Exception + * @return String Generated ETag + */ + public function generate_etag() + { + if ($this->_body === '') + { + throw new Request_Exception('No response yet associated with request - cannot auto generate resource ETag'); + } + + // Generate a unique hash for the response + return '"'.sha1($this->render()).'"'; + } + + /** + * Parse the byte ranges from the HTTP_RANGE header used for + * resumable downloads. + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 + * @return array|FALSE + */ + protected function _parse_byte_range() + { + if ( ! isset($_SERVER['HTTP_RANGE'])) + { + return FALSE; + } + + // TODO, speed this up with the use of string functions. + preg_match_all('/(-?[0-9]++(?:-(?![0-9]++))?)(?:-?([0-9]++))?/', $_SERVER['HTTP_RANGE'], $matches, PREG_SET_ORDER); + + return $matches[0]; + } + + /** + * Calculates the byte range to use with send_file. If HTTP_RANGE doesn't + * exist then the complete byte range is returned + * + * @param integer $size + * @return array + */ + protected function _calculate_byte_range($size) + { + // Defaults to start with when the HTTP_RANGE header doesn't exist. + $start = 0; + $end = $size - 1; + + if ($range = $this->_parse_byte_range()) + { + // We have a byte range from HTTP_RANGE + $start = $range[1]; + + if ($start[0] === '-') + { + // A negative value means we start from the end, so -500 would be the + // last 500 bytes. + $start = $size - abs($start); + } + + if (isset($range[2])) + { + // Set the end range + $end = $range[2]; + } + } + + // Normalize values. + $start = abs(intval($start)); + + // Keep the the end value in bounds and normalize it. + $end = min(abs(intval($end)), $size - 1); + + // Keep the start in bounds. + $start = ($end < $start) ? 0 : max($start, 0); + + return array($start, $end); + } +} // End Kohana_Response diff --git a/system/classes/Kohana/Route.php b/system/classes/Kohana/Route.php new file mode 100644 index 0000000..808d374 --- /dev/null +++ b/system/classes/Kohana/Route.php @@ -0,0 +1,629 @@ + will be translated to a regular expression using a default + * regular expression pattern. You can override the default pattern by providing + * a pattern for the key: + * + * // This route will only match when is a digit + * Route::set('user', 'user//', array('id' => '\d+')); + * + * // This route will match when is anything + * Route::set('file', '', array('path' => '.*')); + * + * It is also possible to create optional segments by using parentheses in + * the URI definition: + * + * // This is the standard default route, and no keys are required + * Route::set('default', '((/(/)))'); + * + * // This route only requires the key + * Route::set('file', '(/)(.)', array('path' => '.*', 'format' => '\w+')); + * + * Routes also provide a way to generate URIs (called "reverse routing"), which + * makes them an extremely powerful and flexible way to generate internal links. + * + * @package Kohana + * @category Base + * @author Kohana Team + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_Route { + + // Defines the pattern of a + const REGEX_KEY = '<([a-zA-Z0-9_]++)>'; + + // What can be part of a value + const REGEX_SEGMENT = '[^/.,;?\n]++'; + + // What must be escaped in the route regex + const REGEX_ESCAPE = '[.\\+*?[^\\]${}=!|]'; + + /** + * @var string default protocol for all routes + * + * @example 'http://' + */ + public static $default_protocol = 'http://'; + + /** + * @var array list of valid localhost entries + */ + public static $localhosts = array(FALSE, '', 'local', 'localhost'); + + /** + * @var string default action for all routes + */ + public static $default_action = 'index'; + + /** + * @var bool Indicates whether routes are cached + */ + public static $cache = FALSE; + + /** + * @var array + */ + protected static $_routes = array(); + + /** + * Stores a named route and returns it. The "action" will always be set to + * "index" if it is not defined. + * + * Route::set('default', '((/(/)))') + * ->defaults(array( + * 'controller' => 'welcome', + * )); + * + * @param string $name route name + * @param string $uri URI pattern + * @param array $regex regex patterns for route keys + * @return Route + */ + public static function set($name, $uri = NULL, $regex = NULL) + { + return Route::$_routes[$name] = new Route($uri, $regex); + } + + /** + * Retrieves a named route. + * + * $route = Route::get('default'); + * + * @param string $name route name + * @return Route + * @throws Kohana_Exception + */ + public static function get($name) + { + if ( ! isset(Route::$_routes[$name])) + { + throw new Kohana_Exception('The requested route does not exist: :route', + array(':route' => $name)); + } + + return Route::$_routes[$name]; + } + + /** + * Retrieves all named routes. + * + * $routes = Route::all(); + * + * @return array routes by name + */ + public static function all() + { + return Route::$_routes; + } + + /** + * Get the name of a route. + * + * $name = Route::name($route) + * + * @param Route $route instance + * @return string + */ + public static function name(Route $route) + { + return array_search($route, Route::$_routes); + } + + /** + * Saves or loads the route cache. If your routes will remain the same for + * a long period of time, use this to reload the routes from the cache + * rather than redefining them on every page load. + * + * if ( ! Route::cache()) + * { + * // Set routes here + * Route::cache(TRUE); + * } + * + * @param boolean $save cache the current routes + * @param boolean $append append, rather than replace, cached routes when loading + * @return void when saving routes + * @return boolean when loading routes + * @uses Kohana::cache + */ + public static function cache($save = FALSE, $append = FALSE) + { + if ($save === TRUE) + { + try + { + // Cache all defined routes + Kohana::cache('Route::cache()', Route::$_routes); + } + catch (Exception $e) + { + // We most likely have a lambda in a route, which cannot be cached + throw new Kohana_Exception('One or more routes could not be cached (:message)', array( + ':message' => $e->getMessage(), + ), 0, $e); + } + } + else + { + if ($routes = Kohana::cache('Route::cache()')) + { + if ($append) + { + // Append cached routes + Route::$_routes += $routes; + } + else + { + // Replace existing routes + Route::$_routes = $routes; + } + + // Routes were cached + return Route::$cache = TRUE; + } + else + { + // Routes were not cached + return Route::$cache = FALSE; + } + } + } + + /** + * Create a URL from a route name. This is a shortcut for: + * + * echo URL::site(Route::get($name)->uri($params), $protocol); + * + * @param string $name route name + * @param array $params URI parameters + * @param mixed $protocol protocol string or boolean, adds protocol and domain + * @return string + * @since 3.0.7 + * @uses URL::site + */ + public static function url($name, array $params = NULL, $protocol = NULL) + { + $route = Route::get($name); + + // Create a URI with the route and convert it to a URL + if ($route->is_external()) + return Route::get($name)->uri($params); + else + return URL::site(Route::get($name)->uri($params), $protocol); + } + + /** + * Returns the compiled regular expression for the route. This translates + * keys and optional groups to a proper PCRE regular expression. + * + * $compiled = Route::compile( + * '(/(/))', + * array( + * 'controller' => '[a-z]+', + * 'id' => '\d+', + * ) + * ); + * + * @return string + * @uses Route::REGEX_ESCAPE + * @uses Route::REGEX_SEGMENT + */ + public static function compile($uri, array $regex = NULL) + { + // The URI should be considered literal except for keys and optional parts + // Escape everything preg_quote would escape except for : ( ) < > + $expression = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $uri); + + if (strpos($expression, '(') !== FALSE) + { + // Make optional parts of the URI non-capturing and optional + $expression = str_replace(array('(', ')'), array('(?:', ')?'), $expression); + } + + // Insert default regex for keys + $expression = str_replace(array('<', '>'), array('(?P<', '>'.Route::REGEX_SEGMENT.')'), $expression); + + if ($regex) + { + $search = $replace = array(); + foreach ($regex as $key => $value) + { + $search[] = "<$key>".Route::REGEX_SEGMENT; + $replace[] = "<$key>$value"; + } + + // Replace the default regex with the user-specified regex + $expression = str_replace($search, $replace, $expression); + } + + return '#^'.$expression.'$#uD'; + } + + /** + * @var array route filters + */ + protected $_filters = array(); + + /** + * @var string route URI + */ + protected $_uri = ''; + + /** + * @var array + */ + protected $_regex = array(); + + /** + * @var array + */ + protected $_defaults = array('action' => 'index', 'host' => FALSE); + + /** + * @var string + */ + protected $_route_regex; + + /** + * Creates a new route. Sets the URI and regular expressions for keys. + * Routes should always be created with [Route::set] or they will not + * be properly stored. + * + * $route = new Route($uri, $regex); + * + * The $uri parameter should be a string for basic regex matching. + * + * + * @param string $uri route URI pattern + * @param array $regex key patterns + * @return void + * @uses Route::_compile + */ + public function __construct($uri = NULL, $regex = NULL) + { + if ($uri === NULL) + { + // Assume the route is from cache + return; + } + + if ( ! empty($uri)) + { + $this->_uri = $uri; + } + + if ( ! empty($regex)) + { + $this->_regex = $regex; + } + + // Store the compiled regex locally + $this->_route_regex = Route::compile($uri, $regex); + } + + /** + * Provides default values for keys when they are not present. The default + * action will always be "index" unless it is overloaded here. + * + * $route->defaults(array( + * 'controller' => 'welcome', + * 'action' => 'index' + * )); + * + * If no parameter is passed, this method will act as a getter. + * + * @param array $defaults key values + * @return $this or array + */ + public function defaults(array $defaults = NULL) + { + if ($defaults === NULL) + { + return $this->_defaults; + } + + $this->_defaults = $defaults; + + return $this; + } + + /** + * Filters to be run before route parameters are returned: + * + * $route->filter( + * function(Route $route, $params, Request $request) + * { + * if ($request->method() !== HTTP_Request::POST) + * { + * return FALSE; // This route only matches POST requests + * } + * if ($params AND $params['controller'] === 'welcome') + * { + * $params['controller'] = 'home'; + * } + * + * return $params; + * } + * ); + * + * To prevent a route from matching, return `FALSE`. To replace the route + * parameters, return an array. + * + * [!!] Default parameters are added before filters are called! + * + * @throws Kohana_Exception + * @param array $callback callback string, array, or closure + * @return $this + */ + public function filter($callback) + { + if ( ! is_callable($callback)) + { + throw new Kohana_Exception('Invalid Route::callback specified'); + } + + $this->_filters[] = $callback; + + return $this; + } + + /** + * Tests if the route matches a given URI. A successful match will return + * all of the routed parameters as an array. A failed match will return + * boolean FALSE. + * + * // Params: controller = users, action = edit, id = 10 + * $params = $route->matches('users/edit/10'); + * + * This method should almost always be used within an if/else block: + * + * if ($params = $route->matches($uri)) + * { + * // Parse the parameters + * } + * + * @param string $uri URI to match + * @return array on success + * @return FALSE on failure + */ + public function matches(Request $request) + { + // Get the URI from the Request + $uri = trim($request->uri(), '/'); + + if ( ! preg_match($this->_route_regex, $uri, $matches)) + return FALSE; + + $params = array(); + foreach ($matches as $key => $value) + { + if (is_int($key)) + { + // Skip all unnamed keys + continue; + } + + // Set the value for all matched keys + $params[$key] = $value; + } + + foreach ($this->_defaults as $key => $value) + { + if ( ! isset($params[$key]) OR $params[$key] === '') + { + // Set default values for any key that was not matched + $params[$key] = $value; + } + } + + if ( ! empty($params['controller'])) + { + // PSR-0: Replace underscores with spaces, run ucwords, then replace underscore + $params['controller'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $params['controller']))); + } + + if ( ! empty($params['directory'])) + { + // PSR-0: Replace underscores with spaces, run ucwords, then replace underscore + $params['directory'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $params['directory']))); + } + + if ($this->_filters) + { + foreach ($this->_filters as $callback) + { + // Execute the filter giving it the route, params, and request + $return = call_user_func($callback, $this, $params, $request); + + if ($return === FALSE) + { + // Filter has aborted the match + return FALSE; + } + elseif (is_array($return)) + { + // Filter has modified the parameters + $params = $return; + } + } + } + + return $params; + } + + /** + * Returns whether this route is an external route + * to a remote controller. + * + * @return boolean + */ + public function is_external() + { + return ! in_array(Arr::get($this->_defaults, 'host', FALSE), Route::$localhosts); + } + + /** + * Generates a URI for the current route based on the parameters given. + * + * // Using the "default" route: "users/profile/10" + * $route->uri(array( + * 'controller' => 'users', + * 'action' => 'profile', + * 'id' => '10' + * )); + * + * @param array $params URI parameters + * @return string + * @throws Kohana_Exception + * @uses Route::REGEX_Key + */ + public function uri(array $params = NULL) + { + // Start with the routed URI + $uri = $this->_uri; + + if (strpos($uri, '<') === FALSE AND strpos($uri, '(') === FALSE) + { + // This is a static route, no need to replace anything + + if ( ! $this->is_external()) + return $uri; + + // If the localhost setting does not have a protocol + if (strpos($this->_defaults['host'], '://') === FALSE) + { + // Use the default defined protocol + $params['host'] = Route::$default_protocol.$this->_defaults['host']; + } + else + { + // Use the supplied host with protocol + $params['host'] = $this->_defaults['host']; + } + + // Compile the final uri and return it + return rtrim($params['host'], '/').'/'.$uri; + } + + // Keep track of whether an optional param was replaced + $provided_optional = FALSE; + + while (preg_match('#\([^()]++\)#', $uri, $match)) + { + + // Search for the matched value + $search = $match[0]; + + // Remove the parenthesis from the match as the replace + $replace = substr($match[0], 1, -1); + + while (preg_match('#'.Route::REGEX_KEY.'#', $replace, $match)) + { + list($key, $param) = $match; + + if (isset($params[$param]) AND $params[$param] !== Arr::get($this->_defaults, $param)) + { + // Future optional params should be required + $provided_optional = TRUE; + + // Replace the key with the parameter value + $replace = str_replace($key, $params[$param], $replace); + } + elseif ($provided_optional) + { + // Look for a default + if (isset($this->_defaults[$param])) + { + $replace = str_replace($key, $this->_defaults[$param], $replace); + } + else + { + // Ungrouped parameters are required + throw new Kohana_Exception('Required route parameter not passed: :param', array( + ':param' => $param, + )); + } + } + else + { + // This group has missing parameters + $replace = ''; + break; + } + } + + // Replace the group in the URI + $uri = str_replace($search, $replace, $uri); + } + + while (preg_match('#'.Route::REGEX_KEY.'#', $uri, $match)) + { + list($key, $param) = $match; + + if ( ! isset($params[$param])) + { + // Look for a default + if (isset($this->_defaults[$param])) + { + $params[$param] = $this->_defaults[$param]; + } + else + { + // Ungrouped parameters are required + throw new Kohana_Exception('Required route parameter not passed: :param', array( + ':param' => $param, + )); + } + } + + $uri = str_replace($key, $params[$param], $uri); + } + + // Trim all extra slashes from the URI + $uri = preg_replace('#//+#', '/', rtrim($uri, '/')); + + if ($this->is_external()) + { + // Need to add the host to the URI + $host = $this->_defaults['host']; + + if (strpos($host, '://') === FALSE) + { + // Use the default defined protocol + $host = Route::$default_protocol.$host; + } + + // Clean up the host and prepend it to the URI + $uri = rtrim($host, '/').'/'.$uri; + } + + return $uri; + } + +} // End Route diff --git a/system/classes/Kohana/Security.php b/system/classes/Kohana/Security.php new file mode 100644 index 0000000..826347f --- /dev/null +++ b/system/classes/Kohana/Security.php @@ -0,0 +1,103 @@ +rules('csrf', array( + * 'not_empty' => NULL, + * 'Security::check' => NULL, + * )); + * + * This provides a basic, but effective, method of preventing CSRF attacks. + * + * @param boolean $new force a new token to be generated? + * @return string + * @uses Session::instance + */ + public static function token($new = FALSE) + { + $session = Session::instance(); + + // Get the current token + $token = $session->get(Security::$token_name); + + if ($new === TRUE OR ! $token) + { + // Generate a new unique token + $token = sha1(uniqid(NULL, TRUE)); + + // Store the new token + $session->set(Security::$token_name, $token); + } + + return $token; + } + + /** + * Check that the given token matches the currently stored security token. + * + * if (Security::check($token)) + * { + * // Pass + * } + * + * @param string $token token to check + * @return boolean + * @uses Security::token + */ + public static function check($token) + { + return Security::token() === $token; + } + + /** + * Remove image tags from a string. + * + * $str = Security::strip_image_tags($str); + * + * @param string $str string to sanitize + * @return string + */ + public static function strip_image_tags($str) + { + return preg_replace('#\s]*)["\']?[^>]*)?>#is', '$1', $str); + } + + /** + * Encodes PHP tags in a string. + * + * $str = Security::encode_php_tags($str); + * + * @param string $str string to sanitize + * @return string + */ + public static function encode_php_tags($str) + { + return str_replace(array(''), array('<?', '?>'), $str); + } + +} // End security diff --git a/system/classes/Kohana/Session.php b/system/classes/Kohana/Session.php new file mode 100644 index 0000000..aafe607 --- /dev/null +++ b/system/classes/Kohana/Session.php @@ -0,0 +1,505 @@ +load('session')->get($type); + + // Set the session class name + $class = 'Session_'.ucfirst($type); + + // Create a new session instance + Session::$instances[$type] = $session = new $class($config, $id); + + // Write the session at shutdown + register_shutdown_function(array($session, 'write')); + } + + return Session::$instances[$type]; + } + + /** + * @var string cookie name + */ + protected $_name = 'session'; + + /** + * @var int cookie lifetime + */ + protected $_lifetime = 0; + + /** + * @var bool encrypt session data? + */ + protected $_encrypted = FALSE; + + /** + * @var array session data + */ + protected $_data = array(); + + /** + * @var bool session destroyed? + */ + protected $_destroyed = FALSE; + + /** + * Overloads the name, lifetime, and encrypted session settings. + * + * [!!] Sessions can only be created using the [Session::instance] method. + * + * @param array $config configuration + * @param string $id session id + * @return void + * @uses Session::read + */ + public function __construct(array $config = NULL, $id = NULL) + { + if (isset($config['name'])) + { + // Cookie name to store the session id in + $this->_name = (string) $config['name']; + } + + if (isset($config['lifetime'])) + { + // Cookie lifetime + $this->_lifetime = (int) $config['lifetime']; + } + + if (isset($config['encrypted'])) + { + if ($config['encrypted'] === TRUE) + { + // Use the default Encrypt instance + $config['encrypted'] = 'default'; + } + + // Enable or disable encryption of data + $this->_encrypted = $config['encrypted']; + } + + // Load the session + $this->read($id); + } + + /** + * Session object is rendered to a serialized string. If encryption is + * enabled, the session will be encrypted. If not, the output string will + * be encoded. + * + * echo $session; + * + * @return string + * @uses Encrypt::encode + */ + public function __toString() + { + // Serialize the data array + $data = $this->_serialize($this->_data); + + if ($this->_encrypted) + { + // Encrypt the data using the default key + $data = Encrypt::instance($this->_encrypted)->encode($data); + } + else + { + // Encode the data + $data = $this->_encode($data); + } + + return $data; + } + + /** + * Returns the current session array. The returned array can also be + * assigned by reference. + * + * // Get a copy of the current session data + * $data = $session->as_array(); + * + * // Assign by reference for modification + * $data =& $session->as_array(); + * + * @return array + */ + public function & as_array() + { + return $this->_data; + } + + /** + * Get the current session id, if the session supports it. + * + * $id = $session->id(); + * + * [!!] Not all session types have ids. + * + * @return string + * @since 3.0.8 + */ + public function id() + { + return NULL; + } + + /** + * Get the current session cookie name. + * + * $name = $session->name(); + * + * @return string + * @since 3.0.8 + */ + public function name() + { + return $this->_name; + } + + /** + * Get a variable from the session array. + * + * $foo = $session->get('foo'); + * + * @param string $key variable name + * @param mixed $default default value to return + * @return mixed + */ + public function get($key, $default = NULL) + { + return array_key_exists($key, $this->_data) ? $this->_data[$key] : $default; + } + + /** + * Get and delete a variable from the session array. + * + * $bar = $session->get_once('bar'); + * + * @param string $key variable name + * @param mixed $default default value to return + * @return mixed + */ + public function get_once($key, $default = NULL) + { + $value = $this->get($key, $default); + + unset($this->_data[$key]); + + return $value; + } + + /** + * Set a variable in the session array. + * + * $session->set('foo', 'bar'); + * + * @param string $key variable name + * @param mixed $value value + * @return $this + */ + public function set($key, $value) + { + $this->_data[$key] = $value; + + return $this; + } + + /** + * Set a variable by reference. + * + * $session->bind('foo', $foo); + * + * @param string $key variable name + * @param mixed $value referenced value + * @return $this + */ + public function bind($key, & $value) + { + $this->_data[$key] =& $value; + + return $this; + } + + /** + * Removes a variable in the session array. + * + * $session->delete('foo'); + * + * @param string $key,... variable name + * @return $this + */ + public function delete($key) + { + $args = func_get_args(); + + foreach ($args as $key) + { + unset($this->_data[$key]); + } + + return $this; + } + + /** + * Loads existing session data. + * + * $session->read(); + * + * @param string $id session id + * @return void + */ + public function read($id = NULL) + { + $data = NULL; + + try + { + if (is_string($data = $this->_read($id))) + { + if ($this->_encrypted) + { + // Decrypt the data using the default key + $data = Encrypt::instance($this->_encrypted)->decode($data); + } + else + { + // Decode the data + $data = $this->_decode($data); + } + + // Unserialize the data + $data = $this->_unserialize($data); + } + else + { + // Ignore these, session is valid, likely no data though. + } + } + catch (Exception $e) + { + // Error reading the session, usually a corrupt session. + throw new Session_Exception('Error reading session data.', NULL, Session_Exception::SESSION_CORRUPT); + } + + if (is_array($data)) + { + // Load the data locally + $this->_data = $data; + } + } + + /** + * Generates a new session id and returns it. + * + * $id = $session->regenerate(); + * + * @return string + */ + public function regenerate() + { + return $this->_regenerate(); + } + + /** + * Sets the last_active timestamp and saves the session. + * + * $session->write(); + * + * [!!] Any errors that occur during session writing will be logged, + * but not displayed, because sessions are written after output has + * been sent. + * + * @return boolean + * @uses Kohana::$log + */ + public function write() + { + if (headers_sent() OR $this->_destroyed) + { + // Session cannot be written when the headers are sent or when + // the session has been destroyed + return FALSE; + } + + // Set the last active timestamp + $this->_data['last_active'] = time(); + + try + { + return $this->_write(); + } + catch (Exception $e) + { + // Log & ignore all errors when a write fails + Kohana::$log->add(Log::ERROR, Kohana_Exception::text($e))->write(); + + return FALSE; + } + } + + /** + * Completely destroy the current session. + * + * $success = $session->destroy(); + * + * @return boolean + */ + public function destroy() + { + if ($this->_destroyed === FALSE) + { + if ($this->_destroyed = $this->_destroy()) + { + // The session has been destroyed, clear all data + $this->_data = array(); + } + } + + return $this->_destroyed; + } + + /** + * Restart the session. + * + * $success = $session->restart(); + * + * @return boolean + */ + public function restart() + { + if ($this->_destroyed === FALSE) + { + // Wipe out the current session. + $this->destroy(); + } + + // Allow the new session to be saved + $this->_destroyed = FALSE; + + return $this->_restart(); + } + + /** + * Serializes the session data. + * + * @param array $data data + * @return string + */ + protected function _serialize($data) + { + return serialize($data); + } + + /** + * Unserializes the session data. + * + * @param string $data data + * @return array + */ + protected function _unserialize($data) + { + return unserialize($data); + } + + /** + * Encodes the session data using [base64_encode]. + * + * @param string $data data + * @return string + */ + protected function _encode($data) + { + return base64_encode($data); + } + + /** + * Decodes the session data using [base64_decode]. + * + * @param string $data data + * @return string + */ + protected function _decode($data) + { + return base64_decode($data); + } + + /** + * Loads the raw session data string and returns it. + * + * @param string $id session id + * @return string + */ + abstract protected function _read($id = NULL); + + /** + * Generate a new session id and return it. + * + * @return string + */ + abstract protected function _regenerate(); + + /** + * Writes the current session. + * + * @return boolean + */ + abstract protected function _write(); + + /** + * Destroys the current session. + * + * @return boolean + */ + abstract protected function _destroy(); + + /** + * Restarts the current session. + * + * @return boolean + */ + abstract protected function _restart(); + +} // End Session diff --git a/system/classes/Kohana/Session/Cookie.php b/system/classes/Kohana/Session/Cookie.php new file mode 100644 index 0000000..270d3bd --- /dev/null +++ b/system/classes/Kohana/Session/Cookie.php @@ -0,0 +1,55 @@ +_name, NULL); + } + + /** + * @return null + */ + protected function _regenerate() + { + // Cookie sessions have no id + return NULL; + } + + /** + * @return bool + */ + protected function _write() + { + return Cookie::set($this->_name, $this->__toString(), $this->_lifetime); + } + + /** + * @return bool + */ + protected function _restart() + { + return TRUE; + } + + /** + * @return bool + */ + protected function _destroy() + { + return Cookie::delete($this->_name); + } + +} // End Session_Cookie diff --git a/system/classes/Kohana/Session/Exception.php b/system/classes/Kohana/Session/Exception.php new file mode 100644 index 0000000..a6adc4f --- /dev/null +++ b/system/classes/Kohana/Session/Exception.php @@ -0,0 +1,11 @@ +_lifetime, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly); + + // Do not allow PHP to send Cache-Control headers + session_cache_limiter(FALSE); + + // Set the session cookie name + session_name($this->_name); + + if ($id) + { + // Set the session id + session_id($id); + } + + // Start the session + session_start(); + + // Use the $_SESSION global for storing data + $this->_data =& $_SESSION; + + return NULL; + } + + /** + * @return string + */ + protected function _regenerate() + { + // Regenerate the session id + session_regenerate_id(); + + return session_id(); + } + + /** + * @return bool + */ + protected function _write() + { + // Write and close the session + session_write_close(); + + return TRUE; + } + + /** + * @return bool + */ + protected function _restart() + { + // Fire up a new session + $status = session_start(); + + // Use the $_SESSION global for storing data + $this->_data =& $_SESSION; + + return $status; + } + + /** + * @return bool + */ + protected function _destroy() + { + // Destroy the current session + session_destroy(); + + // Did destruction work? + $status = ! session_id(); + + if ($status) + { + // Make sure the session cannot be restarted + Cookie::delete($this->_name); + } + + return $status; + } + +} // End Session_Native diff --git a/system/classes/Kohana/Text.php b/system/classes/Kohana/Text.php new file mode 100644 index 0000000..5209586 --- /dev/null +++ b/system/classes/Kohana/Text.php @@ -0,0 +1,686 @@ + 'billion', + 1000000 => 'million', + 1000 => 'thousand', + 100 => 'hundred', + 90 => 'ninety', + 80 => 'eighty', + 70 => 'seventy', + 60 => 'sixty', + 50 => 'fifty', + 40 => 'fourty', + 30 => 'thirty', + 20 => 'twenty', + 19 => 'nineteen', + 18 => 'eighteen', + 17 => 'seventeen', + 16 => 'sixteen', + 15 => 'fifteen', + 14 => 'fourteen', + 13 => 'thirteen', + 12 => 'twelve', + 11 => 'eleven', + 10 => 'ten', + 9 => 'nine', + 8 => 'eight', + 7 => 'seven', + 6 => 'six', + 5 => 'five', + 4 => 'four', + 3 => 'three', + 2 => 'two', + 1 => 'one', + ); + + /** + * Limits a phrase to a given number of words. + * + * $text = Text::limit_words($text); + * + * @param string $str phrase to limit words of + * @param integer $limit number of words to limit to + * @param string $end_char end character or entity + * @return string + */ + public static function limit_words($str, $limit = 100, $end_char = NULL) + { + $limit = (int) $limit; + $end_char = ($end_char === NULL) ? '…' : $end_char; + + if (trim($str) === '') + return $str; + + if ($limit <= 0) + return $end_char; + + preg_match('/^\s*+(?:\S++\s*+){1,'.$limit.'}/u', $str, $matches); + + // Only attach the end character if the matched string is shorter + // than the starting string. + return rtrim($matches[0]).((strlen($matches[0]) === strlen($str)) ? '' : $end_char); + } + + /** + * Limits a phrase to a given number of characters. + * + * $text = Text::limit_chars($text); + * + * @param string $str phrase to limit characters of + * @param integer $limit number of characters to limit to + * @param string $end_char end character or entity + * @param boolean $preserve_words enable or disable the preservation of words while limiting + * @return string + * @uses UTF8::strlen + */ + public static function limit_chars($str, $limit = 100, $end_char = NULL, $preserve_words = FALSE) + { + $end_char = ($end_char === NULL) ? '…' : $end_char; + + $limit = (int) $limit; + + if (trim($str) === '' OR UTF8::strlen($str) <= $limit) + return $str; + + if ($limit <= 0) + return $end_char; + + if ($preserve_words === FALSE) + return rtrim(UTF8::substr($str, 0, $limit)).$end_char; + + // Don't preserve words. The limit is considered the top limit. + // No strings with a length longer than $limit should be returned. + if ( ! preg_match('/^.{0,'.$limit.'}\s/us', $str, $matches)) + return $end_char; + + return rtrim($matches[0]).((strlen($matches[0]) === strlen($str)) ? '' : $end_char); + } + + /** + * Alternates between two or more strings. + * + * echo Text::alternate('one', 'two'); // "one" + * echo Text::alternate('one', 'two'); // "two" + * echo Text::alternate('one', 'two'); // "one" + * + * Note that using multiple iterations of different strings may produce + * unexpected results. + * + * @param string $str,... strings to alternate between + * @return string + */ + public static function alternate() + { + static $i; + + if (func_num_args() === 0) + { + $i = 0; + return ''; + } + + $args = func_get_args(); + return $args[($i++ % count($args))]; + } + + /** + * Generates a random string of a given type and length. + * + * + * $str = Text::random(); // 8 character random string + * + * The following types are supported: + * + * alnum + * : Upper and lower case a-z, 0-9 (default) + * + * alpha + * : Upper and lower case a-z + * + * hexdec + * : Hexadecimal characters a-f, 0-9 + * + * distinct + * : Uppercase characters and numbers that cannot be confused + * + * You can also create a custom type by providing the "pool" of characters + * as the type. + * + * @param string $type a type of pool, or a string of characters to use as the pool + * @param integer $length length of string to return + * @return string + * @uses UTF8::split + */ + public static function random($type = NULL, $length = 8) + { + if ($type === NULL) + { + // Default is to generate an alphanumeric string + $type = 'alnum'; + } + + $utf8 = FALSE; + + switch ($type) + { + case 'alnum': + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + break; + case 'alpha': + $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + break; + case 'hexdec': + $pool = '0123456789abcdef'; + break; + case 'numeric': + $pool = '0123456789'; + break; + case 'nozero': + $pool = '123456789'; + break; + case 'distinct': + $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ'; + break; + default: + $pool = (string) $type; + $utf8 = ! UTF8::is_ascii($pool); + break; + } + + // Split the pool into an array of characters + $pool = ($utf8 === TRUE) ? UTF8::str_split($pool, 1) : str_split($pool, 1); + + // Largest pool key + $max = count($pool) - 1; + + $str = ''; + for ($i = 0; $i < $length; $i++) + { + // Select a random character from the pool and add it to the string + $str .= $pool[mt_rand(0, $max)]; + } + + // Make sure alnum strings contain at least one letter and one digit + if ($type === 'alnum' AND $length > 1) + { + if (ctype_alpha($str)) + { + // Add a random digit + $str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57)); + } + elseif (ctype_digit($str)) + { + // Add a random letter + $str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90)); + } + } + + return $str; + } + + /** + * Uppercase words that are not separated by spaces, using a custom + * delimiter or the default. + * + * $str = Text::ucfirst('content-type'); // returns "Content-Type" + * + * @param string $string string to transform + * @param string $delimiter delemiter to use + * @return string + */ + public static function ucfirst($string, $delimiter = '-') + { + // Put the keys back the Case-Convention expected + return implode($delimiter, array_map('ucfirst', explode($delimiter, $string))); + } + + /** + * Reduces multiple slashes in a string to single slashes. + * + * $str = Text::reduce_slashes('foo//bar/baz'); // "foo/bar/baz" + * + * @param string $str string to reduce slashes of + * @return string + */ + public static function reduce_slashes($str) + { + return preg_replace('#(? '#####', + * )); + * + * @param string $str phrase to replace words in + * @param array $badwords words to replace + * @param string $replacement replacement string + * @param boolean $replace_partial_words replace words across word boundries (space, period, etc) + * @return string + * @uses UTF8::strlen + */ + public static function censor($str, $badwords, $replacement = '#', $replace_partial_words = TRUE) + { + foreach ( (array) $badwords as $key => $badword) + { + $badwords[$key] = str_replace('\*', '\S*?', preg_quote( (string) $badword)); + } + + $regex = '('.implode('|', $badwords).')'; + + if ($replace_partial_words === FALSE) + { + // Just using \b isn't sufficient when we need to replace a badword that already contains word boundaries itself + $regex = '(?<=\b|\s|^)'.$regex.'(?=\b|\s|$)'; + } + + $regex = '!'.$regex.'!ui'; + + if (UTF8::strlen($replacement) == 1) + { + $regex .= 'e'; + return preg_replace($regex, 'str_repeat($replacement, UTF8::strlen(\'$1\'))', $str); + } + + return preg_replace($regex, $replacement, $str); + } + + /** + * Finds the text that is similar between a set of words. + * + * $match = Text::similar(array('fred', 'fran', 'free'); // "fr" + * + * @param array $words words to find similar text of + * @return string + */ + public static function similar(array $words) + { + // First word is the word to match against + $word = current($words); + + for ($i = 0, $max = strlen($word); $i < $max; ++$i) + { + foreach ($words as $w) + { + // Once a difference is found, break out of the loops + if ( ! isset($w[$i]) OR $w[$i] !== $word[$i]) + break 2; + } + } + + // Return the similar text + return substr($word, 0, $i); + } + + /** + * Converts text email addresses and anchors into links. Existing links + * will not be altered. + * + * echo Text::auto_link($text); + * + * [!!] This method is not foolproof since it uses regex to parse HTML. + * + * @param string $text text to auto link + * @return string + * @uses Text::auto_link_urls + * @uses Text::auto_link_emails + */ + public static function auto_link($text) + { + // Auto link emails first to prevent problems with "www.domain.com@example.com" + return Text::auto_link_urls(Text::auto_link_emails($text)); + } + + /** + * Converts text anchors into links. Existing links will not be altered. + * + * echo Text::auto_link_urls($text); + * + * [!!] This method is not foolproof since it uses regex to parse HTML. + * + * @param string $text text to auto link + * @return string + * @uses HTML::anchor + */ + public static function auto_link_urls($text) + { + // Find and replace all http/https/ftp/ftps links that are not part of an existing html anchor + $text = preg_replace_callback('~\b(?)(?:ht|f)tps?://[^<\s]+(?:/|\b)~i', 'Text::_auto_link_urls_callback1', $text); + + // Find and replace all naked www.links.com (without http://) + return preg_replace_callback('~\b(?)www(?:\.[a-z0-9][-a-z0-9]*+)+\.[a-z]{2,6}[^<\s]*\b~i', 'Text::_auto_link_urls_callback2', $text); + } + + protected static function _auto_link_urls_callback1($matches) + { + return HTML::anchor($matches[0]); + } + + protected static function _auto_link_urls_callback2($matches) + { + return HTML::anchor('http://'.$matches[0], $matches[0]); + } + + /** + * Converts text email addresses into links. Existing links will not + * be altered. + * + * echo Text::auto_link_emails($text); + * + * [!!] This method is not foolproof since it uses regex to parse HTML. + * + * @param string $text text to auto link + * @return string + * @uses HTML::mailto + */ + public static function auto_link_emails($text) + { + // Find and replace all email addresses that are not part of an existing html mailto anchor + // Note: The "58;" negative lookbehind prevents matching of existing encoded html mailto anchors + // The html entity for a colon (:) is : or : or : etc. + return preg_replace_callback('~\b(?)~i', 'Text::_auto_link_emails_callback', $text); + } + + protected static function _auto_link_emails_callback($matches) + { + return HTML::mailto($matches[0]); + } + + /** + * Automatically applies "p" and "br" markup to text. + * Basically [nl2br](http://php.net/nl2br) on steroids. + * + * echo Text::auto_p($text); + * + * [!!] This method is not foolproof since it uses regex to parse HTML. + * + * @param string $str subject + * @param boolean $br convert single linebreaks to
    + * @return string + */ + public static function auto_p($str, $br = TRUE) + { + // Trim whitespace + if (($str = trim($str)) === '') + return ''; + + // Standardize newlines + $str = str_replace(array("\r\n", "\r"), "\n", $str); + + // Trim whitespace on each line + $str = preg_replace('~^[ \t]+~m', '', $str); + $str = preg_replace('~[ \t]+$~m', '', $str); + + // The following regexes only need to be executed if the string contains html + if ($html_found = (strpos($str, '<') !== FALSE)) + { + // Elements that should not be surrounded by p tags + $no_p = '(?:p|div|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))'; + + // Put at least two linebreaks before and after $no_p elements + $str = preg_replace('~^<'.$no_p.'[^>]*+>~im', "\n$0", $str); + $str = preg_replace('~$~im', "$0\n", $str); + } + + // Do the

    magic! + $str = '

    '.trim($str).'

    '; + $str = preg_replace('~\n{2,}~', "

    \n\n

    ", $str); + + // The following regexes only need to be executed if the string contains html + if ($html_found !== FALSE) + { + // Remove p tags around $no_p elements + $str = preg_replace('~

    (?=]*+>)~i', '', $str); + $str = preg_replace('~(]*+>)

    ~i', '$1', $str); + } + + // Convert single linebreaks to
    + if ($br === TRUE) + { + $str = preg_replace('~(?\n", $str); + } + + return $str; + } + + /** + * Returns human readable sizes. Based on original functions written by + * [Aidan Lister](http://aidanlister.com/repos/v/function.size_readable.php) + * and [Quentin Zervaas](http://www.phpriot.com/d/code/strings/filesize-format/). + * + * echo Text::bytes(filesize($file)); + * + * @param integer $bytes size in bytes + * @param string $force_unit a definitive unit + * @param string $format the return string format + * @param boolean $si whether to use SI prefixes or IEC + * @return string + */ + public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE) + { + // Format string + $format = ($format === NULL) ? '%01.2f %s' : (string) $format; + + // IEC prefixes (binary) + if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE) + { + $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'); + $mod = 1024; + } + // SI prefixes (decimal) + else + { + $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB'); + $mod = 1000; + } + + // Determine unit to use + if (($power = array_search( (string) $force_unit, $units)) === FALSE) + { + $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0; + } + + return sprintf($format, $bytes / pow($mod, $power), $units[$power]); + } + + /** + * Format a number to human-readable text. + * + * // Display: one thousand and twenty-four + * echo Text::number(1024); + * + * // Display: five million, six hundred and thirty-two + * echo Text::number(5000632); + * + * @param integer $number number to format + * @return string + * @since 3.0.8 + */ + public static function number($number) + { + // The number must always be an integer + $number = (int) $number; + + // Uncompiled text version + $text = array(); + + // Last matched unit within the loop + $last_unit = NULL; + + // The last matched item within the loop + $last_item = ''; + + foreach (Text::$units as $unit => $name) + { + if ($number / $unit >= 1) + { + // $value = the number of times the number is divisble by unit + $number -= $unit * ($value = (int) floor($number / $unit)); + // Temporary var for textifying the current unit + $item = ''; + + if ($unit < 100) + { + if ($last_unit < 100 AND $last_unit >= 20) + { + $last_item .= '-'.$name; + } + else + { + $item = $name; + } + } + else + { + $item = Text::number($value).' '.$name; + } + + // In the situation that we need to make a composite number (i.e. twenty-three) + // then we need to modify the previous entry + if (empty($item)) + { + array_pop($text); + + $item = $last_item; + } + + $last_item = $text[] = $item; + $last_unit = $unit; + } + } + + if (count($text) > 1) + { + $and = array_pop($text); + } + + $text = implode(', ', $text); + + if (isset($and)) + { + $text .= ' and '.$and; + } + + return $text; + } + + /** + * Prevents [widow words](http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin) + * by inserting a non-breaking space between the last two words. + * + * echo Text::widont($text); + * + * @param string $str text to remove widows from + * @return string + */ + public static function widont($str) + { + $str = rtrim($str); + $space = strrpos($str, ' '); + + if ($space !== FALSE) + { + $str = substr($str, 0, $space).' '.substr($str, $space + 1); + } + + return $str; + } + + /** + * Returns information about the client user agent. + * + * // Returns "Chrome" when using Google Chrome + * $browser = Text::user_agent('browser'); + * + * Multiple values can be returned at once by using an array: + * + * // Get the browser and platform with a single call + * $info = Text::user_agent(array('browser', 'platform')); + * + * When using an array for the value, an associative array will be returned. + * + * @param mixed $value array or string to return: browser, version, robot, mobile, platform + * @return mixed requested information, FALSE if nothing is found + * @uses Kohana::$config + */ + public static function user_agent($agent, $value) + { + if (is_array($value)) + { + $data = array(); + foreach ($value as $part) + { + // Add each part to the set + $data[$part] = Text::user_agent($agent, $part); + } + + return $data; + } + + if ($value === 'browser' OR $value == 'version') + { + // Extra data will be captured + $info = array(); + + // Load browsers + $browsers = Kohana::$config->load('user_agents')->browser; + + foreach ($browsers as $search => $name) + { + if (stripos($agent, $search) !== FALSE) + { + // Set the browser name + $info['browser'] = $name; + + if (preg_match('#'.preg_quote($search).'[^0-9.]*+([0-9.][0-9.a-z]*)#i', Request::$user_agent, $matches)) + { + // Set the version number + $info['version'] = $matches[1]; + } + else + { + // No version number found + $info['version'] = FALSE; + } + + return $info[$value]; + } + } + } + else + { + // Load the search group for this type + $group = Kohana::$config->load('user_agents')->$value; + + foreach ($group as $search => $name) + { + if (stripos($agent, $search) !== FALSE) + { + // Set the value name + return $name; + } + } + } + + // The value requested could not be found + return FALSE; + } + +} // End text diff --git a/system/classes/Kohana/URL.php b/system/classes/Kohana/URL.php new file mode 100644 index 0000000..390a6a0 --- /dev/null +++ b/system/classes/Kohana/URL.php @@ -0,0 +1,213 @@ +secure()) + { + // Use the current protocol + list($protocol) = explode('/', strtolower($protocol->protocol())); + } + else + { + $protocol = 'https'; + } + } + + if ( ! $protocol) + { + // Use the configured default protocol + $protocol = parse_url($base_url, PHP_URL_SCHEME); + } + + if ($index === TRUE AND ! empty(Kohana::$index_file)) + { + // Add the index file to the URL + $base_url .= Kohana::$index_file.'/'; + } + + if (is_string($protocol)) + { + if ($port = parse_url($base_url, PHP_URL_PORT)) + { + // Found a port, make it usable for the URL + $port = ':'.$port; + } + + if ($domain = parse_url($base_url, PHP_URL_HOST)) + { + // Remove everything but the path from the URL + $base_url = parse_url($base_url, PHP_URL_PATH); + } + else + { + // Attempt to use HTTP_HOST and fallback to SERVER_NAME + $domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']; + } + + // Add the protocol and domain to the base URL + $base_url = $protocol.'://'.$domain.$port.$base_url; + } + + return $base_url; + } + + /** + * Fetches an absolute site URL based on a URI segment. + * + * echo URL::site('foo/bar'); + * + * @param string $uri Site URI to convert + * @param mixed $protocol Protocol string or [Request] class to use protocol from + * @param boolean $index Include the index_page in the URL + * @return string + * @uses URL::base + */ + public static function site($uri = '', $protocol = NULL, $index = TRUE) + { + // Chop off possible scheme, host, port, user and pass parts + $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); + + if ( ! UTF8::is_ascii($path)) + { + // Encode all non-ASCII characters, as per RFC 1738 + $path = preg_replace_callback('~([^/]+)~', 'URL::_rawurlencode_callback', $path); + } + + // Concat the URL + return URL::base($protocol, $index).$path; + } + + /** + * Callback used for encoding all non-ASCII characters, as per RFC 1738 + * Used by URL::site() + * + * @param array $matches Array of matches from preg_replace_callback() + * @return string Encoded string + */ + protected static function _rawurlencode_callback($matches) + { + return rawurlencode($matches[0]); + } + + /** + * Merges the current GET parameters with an array of new or overloaded + * parameters and returns the resulting query string. + * + * // Returns "?sort=title&limit=10" combined with any existing GET values + * $query = URL::query(array('sort' => 'title', 'limit' => 10)); + * + * Typically you would use this when you are sorting query results, + * or something similar. + * + * [!!] Parameters with a NULL value are left out. + * + * @param array $params Array of GET parameters + * @param boolean $use_get Include current request GET parameters + * @return string + */ + public static function query(array $params = NULL, $use_get = TRUE) + { + if ($use_get) + { + if ($params === NULL) + { + // Use only the current parameters + $params = $_GET; + } + else + { + // Merge the current and new parameters + $params = Arr::merge($_GET, $params); + } + } + + if (empty($params)) + { + // No query parameters + return ''; + } + + // Note: http_build_query returns an empty string for a params array with only NULL values + $query = http_build_query($params, '', '&'); + + // Don't prepend '?' to an empty string + return ($query === '') ? '' : ('?'.$query); + } + + /** + * Convert a phrase to a URL-safe title. + * + * echo URL::title('My Blog Post'); // "my-blog-post" + * + * @param string $title Phrase to convert + * @param string $separator Word separator (any single character) + * @param boolean $ascii_only Transliterate to ASCII? + * @return string + * @uses UTF8::transliterate_to_ascii + */ + public static function title($title, $separator = '-', $ascii_only = FALSE) + { + if ($ascii_only === TRUE) + { + // Transliterate non-ASCII characters + $title = UTF8::transliterate_to_ascii($title); + + // Remove all characters that are not the separator, a-z, 0-9, or whitespace + $title = preg_replace('![^'.preg_quote($separator).'a-z0-9\s]+!', '', strtolower($title)); + } + else + { + // Remove all characters that are not the separator, letters, numbers, or whitespace + $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', UTF8::strtolower($title)); + } + + // Replace all separator characters and whitespace by a single separator + $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); + + // Trim separators from the beginning and end + return trim($title, $separator); + } + +} // End url diff --git a/system/classes/Kohana/UTF8.php b/system/classes/Kohana/UTF8.php new file mode 100644 index 0000000..26e18de --- /dev/null +++ b/system/classes/Kohana/UTF8.php @@ -0,0 +1,767 @@ + $val) + { + // Recursion! + $var[self::clean($key)] = self::clean($val); + } + } + elseif (is_string($var) AND $var !== '') + { + // Remove control characters + $var = self::strip_ascii_ctrl($var); + + if ( ! self::is_ascii($var)) + { + // Disable notices + $error_reporting = error_reporting(~E_NOTICE); + + // iconv is expensive, so it is only used when needed + $var = iconv($charset, $charset.'//IGNORE', $var); + + // Turn notices back on + error_reporting($error_reporting); + } + } + + return $var; + } + + /** + * Tests whether a string contains only 7-bit ASCII bytes. This is used to + * determine when to use native functions or UTF-8 functions. + * + * $ascii = UTF8::is_ascii($str); + * + * @param mixed $str string or array of strings to check + * @return boolean + */ + public static function is_ascii($str) + { + if (is_array($str)) + { + $str = implode($str); + } + + return ! preg_match('/[^\x00-\x7F]/S', $str); + } + + /** + * Strips out device control codes in the ASCII range. + * + * $str = UTF8::strip_ascii_ctrl($str); + * + * @param string $str string to clean + * @return string + */ + public static function strip_ascii_ctrl($str) + { + return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $str); + } + + /** + * Strips out all non-7bit ASCII bytes. + * + * $str = UTF8::strip_non_ascii($str); + * + * @param string $str string to clean + * @return string + */ + public static function strip_non_ascii($str) + { + return preg_replace('/[^\x00-\x7F]+/S', '', $str); + } + + /** + * Replaces special/accented UTF-8 characters by ASCII-7 "equivalents". + * + * $ascii = UTF8::transliterate_to_ascii($utf8); + * + * @author Andreas Gohr + * @param string $str string to transliterate + * @param integer $case -1 lowercase only, +1 uppercase only, 0 both cases + * @return string + */ + public static function transliterate_to_ascii($str, $case = 0) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _transliterate_to_ascii($str, $case); + } + + /** + * Returns the length of the given string. This is a UTF8-aware version + * of [strlen](http://php.net/strlen). + * + * $length = UTF8::strlen($str); + * + * @param string $str string being measured for length + * @return integer + * @uses UTF8::$server_utf8 + */ + public static function strlen($str) + { + if (UTF8::$server_utf8) + return mb_strlen($str, Kohana::$charset); + + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strlen($str); + } + + /** + * Finds position of first occurrence of a UTF-8 string. This is a + * UTF8-aware version of [strpos](http://php.net/strpos). + * + * $position = UTF8::strpos($str, $search); + * + * @author Harry Fuecks + * @param string $str haystack + * @param string $search needle + * @param integer $offset offset from which character in haystack to start searching + * @return integer position of needle + * @return boolean FALSE if the needle is not found + * @uses UTF8::$server_utf8 + */ + public static function strpos($str, $search, $offset = 0) + { + if (UTF8::$server_utf8) + return mb_strpos($str, $search, $offset, Kohana::$charset); + + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strpos($str, $search, $offset); + } + + /** + * Finds position of last occurrence of a char in a UTF-8 string. This is + * a UTF8-aware version of [strrpos](http://php.net/strrpos). + * + * $position = UTF8::strrpos($str, $search); + * + * @author Harry Fuecks + * @param string $str haystack + * @param string $search needle + * @param integer $offset offset from which character in haystack to start searching + * @return integer position of needle + * @return boolean FALSE if the needle is not found + * @uses UTF8::$server_utf8 + */ + public static function strrpos($str, $search, $offset = 0) + { + if (UTF8::$server_utf8) + return mb_strrpos($str, $search, $offset, Kohana::$charset); + + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strrpos($str, $search, $offset); + } + + /** + * Returns part of a UTF-8 string. This is a UTF8-aware version + * of [substr](http://php.net/substr). + * + * $sub = UTF8::substr($str, $offset); + * + * @author Chris Smith + * @param string $str input string + * @param integer $offset offset + * @param integer $length length limit + * @return string + * @uses UTF8::$server_utf8 + * @uses Kohana::$charset + */ + public static function substr($str, $offset, $length = NULL) + { + if (UTF8::$server_utf8) + return ($length === NULL) + ? mb_substr($str, $offset, mb_strlen($str), Kohana::$charset) + : mb_substr($str, $offset, $length, Kohana::$charset); + + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _substr($str, $offset, $length); + } + + /** + * Replaces text within a portion of a UTF-8 string. This is a UTF8-aware + * version of [substr_replace](http://php.net/substr_replace). + * + * $str = UTF8::substr_replace($str, $replacement, $offset); + * + * @author Harry Fuecks + * @param string $str input string + * @param string $replacement replacement string + * @param integer $offset offset + * @return string + */ + public static function substr_replace($str, $replacement, $offset, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _substr_replace($str, $replacement, $offset, $length); + } + + /** + * Makes a UTF-8 string lowercase. This is a UTF8-aware version + * of [strtolower](http://php.net/strtolower). + * + * $str = UTF8::strtolower($str); + * + * @author Andreas Gohr + * @param string $str mixed case string + * @return string + * @uses UTF8::$server_utf8 + */ + public static function strtolower($str) + { + if (UTF8::$server_utf8) + return mb_strtolower($str, Kohana::$charset); + + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strtolower($str); + } + + /** + * Makes a UTF-8 string uppercase. This is a UTF8-aware version + * of [strtoupper](http://php.net/strtoupper). + * + * @author Andreas Gohr + * @param string $str mixed case string + * @return string + * @uses UTF8::$server_utf8 + * @uses Kohana::$charset + */ + public static function strtoupper($str) + { + if (UTF8::$server_utf8) + return mb_strtoupper($str, Kohana::$charset); + + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strtoupper($str); + } + + /** + * Makes a UTF-8 string's first character uppercase. This is a UTF8-aware + * version of [ucfirst](http://php.net/ucfirst). + * + * $str = UTF8::ucfirst($str); + * + * @author Harry Fuecks + * @param string $str mixed case string + * @return string + */ + public static function ucfirst($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ucfirst($str); + } + + /** + * Makes the first character of every word in a UTF-8 string uppercase. + * This is a UTF8-aware version of [ucwords](http://php.net/ucwords). + * + * $str = UTF8::ucwords($str); + * + * @author Harry Fuecks + * @param string $str mixed case string + * @return string + * @uses UTF8::$server_utf8 + */ + public static function ucwords($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ucwords($str); + } + + /** + * Case-insensitive UTF-8 string comparison. This is a UTF8-aware version + * of [strcasecmp](http://php.net/strcasecmp). + * + * $compare = UTF8::strcasecmp($str1, $str2); + * + * @author Harry Fuecks + * @param string $str1 string to compare + * @param string $str2 string to compare + * @return integer less than 0 if str1 is less than str2 + * @return integer greater than 0 if str1 is greater than str2 + * @return integer 0 if they are equal + */ + public static function strcasecmp($str1, $str2) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strcasecmp($str1, $str2); + } + + /** + * Returns a string or an array with all occurrences of search in subject + * (ignoring case) and replaced with the given replace value. This is a + * UTF8-aware version of [str_ireplace](http://php.net/str_ireplace). + * + * [!!] This function is very slow compared to the native version. Avoid + * using it when possible. + * + * @author Harry Fuecks + * @param string $str input string + * @param string $search needle + * @return string matched substring if found + * @return FALSE if the substring was not found + */ + public static function stristr($str, $search) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _stristr($str, $search); + } + + /** + * Finds the length of the initial segment matching mask. This is a + * UTF8-aware version of [strspn](http://php.net/strspn). + * + * $found = UTF8::strspn($str, $mask); + * + * @author Harry Fuecks + * @param string $str input string + * @param string $mask mask for search + * @param integer $offset start position of the string to examine + * @param integer $length length of the string to examine + * @return integer length of the initial segment that contains characters in the mask + */ + public static function strspn($str, $mask, $offset = NULL, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strspn($str, $mask, $offset, $length); + } + + /** + * Finds the length of the initial segment not matching mask. This is a + * UTF8-aware version of [strcspn](http://php.net/strcspn). + * + * $found = UTF8::strcspn($str, $mask); + * + * @author Harry Fuecks + * @param string $str input string + * @param string $mask mask for search + * @param integer $offset start position of the string to examine + * @param integer $length length of the string to examine + * @return integer length of the initial segment that contains characters not in the mask + */ + public static function strcspn($str, $mask, $offset = NULL, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strcspn($str, $mask, $offset, $length); + } + + /** + * Pads a UTF-8 string to a certain length with another string. This is a + * UTF8-aware version of [str_pad](http://php.net/str_pad). + * + * $str = UTF8::str_pad($str, $length); + * + * @author Harry Fuecks + * @param string $str input string + * @param integer $final_str_length desired string length after padding + * @param string $pad_str string to use as padding + * @param string $pad_type padding type: STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH + * @return string + */ + public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_type = STR_PAD_RIGHT) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _str_pad($str, $final_str_length, $pad_str, $pad_type); + } + + /** + * Converts a UTF-8 string to an array. This is a UTF8-aware version of + * [str_split](http://php.net/str_split). + * + * $array = UTF8::str_split($str); + * + * @author Harry Fuecks + * @param string $str input string + * @param integer $split_length maximum length of each chunk + * @return array + */ + public static function str_split($str, $split_length = 1) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _str_split($str, $split_length); + } + + /** + * Reverses a UTF-8 string. This is a UTF8-aware version of [strrev](http://php.net/strrev). + * + * $str = UTF8::strrev($str); + * + * @author Harry Fuecks + * @param string $str string to be reversed + * @return string + */ + public static function strrev($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strrev($str); + } + + /** + * Strips whitespace (or other UTF-8 characters) from the beginning and + * end of a string. This is a UTF8-aware version of [trim](http://php.net/trim). + * + * $str = UTF8::trim($str); + * + * @author Andreas Gohr + * @param string $str input string + * @param string $charlist string of characters to remove + * @return string + */ + public static function trim($str, $charlist = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _trim($str, $charlist); + } + + /** + * Strips whitespace (or other UTF-8 characters) from the beginning of + * a string. This is a UTF8-aware version of [ltrim](http://php.net/ltrim). + * + * $str = UTF8::ltrim($str); + * + * @author Andreas Gohr + * @param string $str input string + * @param string $charlist string of characters to remove + * @return string + */ + public static function ltrim($str, $charlist = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ltrim($str, $charlist); + } + + /** + * Strips whitespace (or other UTF-8 characters) from the end of a string. + * This is a UTF8-aware version of [rtrim](http://php.net/rtrim). + * + * $str = UTF8::rtrim($str); + * + * @author Andreas Gohr + * @param string $str input string + * @param string $charlist string of characters to remove + * @return string + */ + public static function rtrim($str, $charlist = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _rtrim($str, $charlist); + } + + /** + * Returns the unicode ordinal for a character. This is a UTF8-aware + * version of [ord](http://php.net/ord). + * + * $digit = UTF8::ord($character); + * + * @author Harry Fuecks + * @param string $chr UTF-8 encoded character + * @return integer + */ + public static function ord($chr) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ord($chr); + } + + /** + * Takes an UTF-8 string and returns an array of ints representing the Unicode characters. + * Astral planes are supported i.e. the ints in the output can be > 0xFFFF. + * Occurrences of the BOM are ignored. Surrogates are not allowed. + * + * $array = UTF8::to_unicode($str); + * + * The Original Code is Mozilla Communicator client code. + * The Initial Developer of the Original Code is Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. + * Ported to PHP by Henri Sivonen , see + * Slight modifications to fit with phputf8 library by Harry Fuecks + * + * @param string $str UTF-8 encoded string + * @return array unicode code points + * @return FALSE if the string is invalid + */ + public static function to_unicode($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _to_unicode($str); + } + + /** + * Takes an array of ints representing the Unicode characters and returns a UTF-8 string. + * Astral planes are supported i.e. the ints in the input can be > 0xFFFF. + * Occurrances of the BOM are ignored. Surrogates are not allowed. + * + * $str = UTF8::to_unicode($array); + * + * The Original Code is Mozilla Communicator client code. + * The Initial Developer of the Original Code is Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. + * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/ + * Slight modifications to fit with phputf8 library by Harry Fuecks . + * + * @param array $str unicode code points representing a string + * @return string utf8 string of characters + * @return boolean FALSE if a code point cannot be found + */ + public static function from_unicode($arr) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require Kohana::find_file('utf8', __FUNCTION__); + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _from_unicode($arr); + } + +} // End UTF8 + +if (Kohana_UTF8::$server_utf8 === NULL) +{ + // Determine if this server supports UTF-8 natively + Kohana_UTF8::$server_utf8 = extension_loaded('mbstring'); +} diff --git a/system/classes/Kohana/UTF8/Exception.php b/system/classes/Kohana/UTF8/Exception.php new file mode 100644 index 0000000..6f640fe --- /dev/null +++ b/system/classes/Kohana/UTF8/Exception.php @@ -0,0 +1,9 @@ +check()) + * { + * // Upload is valid, save it + * Upload::save($array['file']); + * } + * + * @param array $file uploaded file data + * @param string $filename new filename + * @param string $directory new directory + * @param integer $chmod chmod mask + * @return string on success, full path to new file + * @return FALSE on failure + */ + public static function save(array $file, $filename = NULL, $directory = NULL, $chmod = 0644) + { + if ( ! isset($file['tmp_name']) OR ! is_uploaded_file($file['tmp_name'])) + { + // Ignore corrupted uploads + return FALSE; + } + + if ($filename === NULL) + { + // Use the default filename, with a timestamp pre-pended + $filename = uniqid().$file['name']; + } + + if (Upload::$remove_spaces === TRUE) + { + // Remove spaces from the filename + $filename = preg_replace('/\s+/u', '_', $filename); + } + + if ($directory === NULL) + { + // Use the pre-configured upload directory + $directory = Upload::$default_directory; + } + + if ( ! is_dir($directory) OR ! is_writable(realpath($directory))) + { + throw new Kohana_Exception('Directory :dir must be writable', + array(':dir' => Debug::path($directory))); + } + + // Make the filename into a complete path + $filename = realpath($directory).DIRECTORY_SEPARATOR.$filename; + + if (move_uploaded_file($file['tmp_name'], $filename)) + { + if ($chmod !== FALSE) + { + // Set permissions on filename + chmod($filename, $chmod); + } + + // Return new file path + return $filename; + } + + return FALSE; + } + + /** + * Tests if upload data is valid, even if no file was uploaded. If you + * _do_ require a file to be uploaded, add the [Upload::not_empty] rule + * before this rule. + * + * $array->rule('file', 'Upload::valid') + * + * @param array $file $_FILES item + * @return bool + */ + public static function valid($file) + { + return (isset($file['error']) + AND isset($file['name']) + AND isset($file['type']) + AND isset($file['tmp_name']) + AND isset($file['size'])); + } + + /** + * Tests if a successful upload has been made. + * + * $array->rule('file', 'Upload::not_empty'); + * + * @param array $file $_FILES item + * @return bool + */ + public static function not_empty(array $file) + { + return (isset($file['error']) + AND isset($file['tmp_name']) + AND $file['error'] === UPLOAD_ERR_OK + AND is_uploaded_file($file['tmp_name'])); + } + + /** + * Test if an uploaded file is an allowed file type, by extension. + * + * $array->rule('file', 'Upload::type', array(':value', array('jpg', 'png', 'gif'))); + * + * @param array $file $_FILES item + * @param array $allowed allowed file extensions + * @return bool + */ + public static function type(array $file, array $allowed) + { + if ($file['error'] !== UPLOAD_ERR_OK) + return TRUE; + + $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + + return in_array($ext, $allowed); + } + + /** + * Validation rule to test if an uploaded file is allowed by file size. + * File sizes are defined as: SB, where S is the size (1, 8.5, 300, etc.) + * and B is the byte unit (K, MiB, GB, etc.). All valid byte units are + * defined in Num::$byte_units + * + * $array->rule('file', 'Upload::size', array(':value', '1M')) + * $array->rule('file', 'Upload::size', array(':value', '2.5KiB')) + * + * @param array $file $_FILES item + * @param string $size maximum file size allowed + * @return bool + */ + public static function size(array $file, $size) + { + if ($file['error'] === UPLOAD_ERR_INI_SIZE) + { + // Upload is larger than PHP allowed size (upload_max_filesize) + return FALSE; + } + + if ($file['error'] !== UPLOAD_ERR_OK) + { + // The upload failed, no size to check + return TRUE; + } + + // Convert the provided size to bytes for comparison + $size = Num::bytes($size); + + // Test that the file is under or equal to the max size + return ($file['size'] <= $size); + } + + /** + * Validation rule to test if an upload is an image and, optionally, is the correct size. + * + * // The "image" file must be an image + * $array->rule('image', 'Upload::image') + * + * // The "photo" file has a maximum size of 640x480 pixels + * $array->rule('photo', 'Upload::image', array(':value', 640, 480)); + * + * // The "image" file must be exactly 100x100 pixels + * $array->rule('image', 'Upload::image', array(':value', 100, 100, TRUE)); + * + * + * @param array $file $_FILES item + * @param integer $max_width maximum width of image + * @param integer $max_height maximum height of image + * @param boolean $exact match width and height exactly? + * @return boolean + */ + public static function image(array $file, $max_width = NULL, $max_height = NULL, $exact = FALSE) + { + if (Upload::not_empty($file)) + { + try + { + // Get the width and height from the uploaded image + list($width, $height) = getimagesize($file['tmp_name']); + } + catch (ErrorException $e) + { + // Ignore read errors + } + + if (empty($width) OR empty($height)) + { + // Cannot get image size, cannot validate + return FALSE; + } + + if ( ! $max_width) + { + // No limit, use the image width + $max_width = $width; + } + + if ( ! $max_height) + { + // No limit, use the image height + $max_height = $height; + } + + if ($exact) + { + // Check if dimensions match exactly + return ($width === $max_width AND $height === $max_height); + } + else + { + // Check if size is within maximum dimensions + return ($width <= $max_width AND $height <= $max_height); + } + } + + return FALSE; + } + +} // End upload diff --git a/system/classes/Kohana/Valid.php b/system/classes/Kohana/Valid.php new file mode 100644 index 0000000..5e526e6 --- /dev/null +++ b/system/classes/Kohana/Valid.php @@ -0,0 +1,551 @@ +getArrayCopy(); + } + + // Value cannot be NULL, FALSE, '', or an empty array + return ! in_array($value, array(NULL, FALSE, '', array()), TRUE); + } + + /** + * Checks a field against a regular expression. + * + * @param string $value value + * @param string $expression regular expression to match (including delimiters) + * @return boolean + */ + public static function regex($value, $expression) + { + return (bool) preg_match($expression, (string) $value); + } + + /** + * Checks that a field is long enough. + * + * @param string $value value + * @param integer $length minimum length required + * @return boolean + */ + public static function min_length($value, $length) + { + return UTF8::strlen($value) >= $length; + } + + /** + * Checks that a field is short enough. + * + * @param string $value value + * @param integer $length maximum length required + * @return boolean + */ + public static function max_length($value, $length) + { + return UTF8::strlen($value) <= $length; + } + + /** + * Checks that a field is exactly the right length. + * + * @param string $value value + * @param integer|array $length exact length required, or array of valid lengths + * @return boolean + */ + public static function exact_length($value, $length) + { + if (is_array($length)) + { + foreach ($length as $strlen) + { + if (UTF8::strlen($value) === $strlen) + return TRUE; + } + return FALSE; + } + + return UTF8::strlen($value) === $length; + } + + /** + * Checks that a field is exactly the value required. + * + * @param string $value value + * @param string $required required value + * @return boolean + */ + public static function equals($value, $required) + { + return ($value === $required); + } + + /** + * Check an email address for correct format. + * + * @link http://www.iamcal.com/publish/articles/php/parsing_email/ + * @link http://www.w3.org/Protocols/rfc822/ + * + * @param string $email email address + * @param boolean $strict strict RFC compatibility + * @return boolean + */ + public static function email($email, $strict = FALSE) + { + if (UTF8::strlen($email) > 254) + { + return FALSE; + } + + if ($strict === TRUE) + { + $qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]'; + $dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]'; + $atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+'; + $pair = '\\x5c[\\x00-\\x7f]'; + + $domain_literal = "\\x5b($dtext|$pair)*\\x5d"; + $quoted_string = "\\x22($qtext|$pair)*\\x22"; + $sub_domain = "($atom|$domain_literal)"; + $word = "($atom|$quoted_string)"; + $domain = "$sub_domain(\\x2e$sub_domain)*"; + $local_part = "$word(\\x2e$word)*"; + + $expression = "/^$local_part\\x40$domain$/D"; + } + else + { + $expression = '/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*+@(?:(?![-.])[-a-z0-9.]+(? 253) + return FALSE; + + // An extra check for the top level domain + // It must start with a letter + $tld = ltrim(substr($matches[1], (int) strrpos($matches[1], '.')), '.'); + return ctype_alpha($tld[0]); + } + + /** + * Validate an IP. + * + * @param string $ip IP address + * @param boolean $allow_private allow private IP networks + * @return boolean + */ + public static function ip($ip, $allow_private = TRUE) + { + // Do not allow reserved addresses + $flags = FILTER_FLAG_NO_RES_RANGE; + + if ($allow_private === FALSE) + { + // Do not allow private or reserved addresses + $flags = $flags | FILTER_FLAG_NO_PRIV_RANGE; + } + + return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flags); + } + + /** + * Validates a credit card number, with a Luhn check if possible. + * + * @param integer $number credit card number + * @param string|array $type card type, or an array of card types + * @return boolean + * @uses Valid::luhn + */ + public static function credit_card($number, $type = NULL) + { + // Remove all non-digit characters from the number + if (($number = preg_replace('/\D+/', '', $number)) === '') + return FALSE; + + if ($type == NULL) + { + // Use the default type + $type = 'default'; + } + elseif (is_array($type)) + { + foreach ($type as $t) + { + // Test each type for validity + if (Valid::credit_card($number, $t)) + return TRUE; + } + + return FALSE; + } + + $cards = Kohana::$config->load('credit_cards'); + + // Check card type + $type = strtolower($type); + + if ( ! isset($cards[$type])) + return FALSE; + + // Check card number length + $length = strlen($number); + + // Validate the card length by the card type + if ( ! in_array($length, preg_split('/\D+/', $cards[$type]['length']))) + return FALSE; + + // Check card number prefix + if ( ! preg_match('/^'.$cards[$type]['prefix'].'/', $number)) + return FALSE; + + // No Luhn check required + if ($cards[$type]['luhn'] == FALSE) + return TRUE; + + return Valid::luhn($number); + } + + /** + * Validate a number against the [Luhn](http://en.wikipedia.org/wiki/Luhn_algorithm) + * (mod10) formula. + * + * @param string $number number to check + * @return boolean + */ + public static function luhn($number) + { + // Force the value to be a string as this method uses string functions. + // Converting to an integer may pass PHP_INT_MAX and result in an error! + $number = (string) $number; + + if ( ! ctype_digit($number)) + { + // Luhn can only be used on numbers! + return FALSE; + } + + // Check number length + $length = strlen($number); + + // Checksum of the card number + $checksum = 0; + + for ($i = $length - 1; $i >= 0; $i -= 2) + { + // Add up every 2nd digit, starting from the right + $checksum += substr($number, $i, 1); + } + + for ($i = $length - 2; $i >= 0; $i -= 2) + { + // Add up every 2nd digit doubled, starting from the right + $double = substr($number, $i, 1) * 2; + + // Subtract 9 from the double where value is greater than 10 + $checksum += ($double >= 10) ? ($double - 9) : $double; + } + + // If the checksum is a multiple of 10, the number is valid + return ($checksum % 10 === 0); + } + + /** + * Checks if a phone number is valid. + * + * @param string $number phone number to check + * @param array $lengths + * @return boolean + */ + public static function phone($number, $lengths = NULL) + { + if ( ! is_array($lengths)) + { + $lengths = array(7,10,11); + } + + // Remove all non-digit characters from the number + $number = preg_replace('/\D+/', '', $number); + + // Check if the number is within range + return in_array(strlen($number), $lengths); + } + + /** + * Tests if a string is a valid date string. + * + * @param string $str date to check + * @return boolean + */ + public static function date($str) + { + return (strtotime($str) !== FALSE); + } + + /** + * Checks whether a string consists of alphabetical characters only. + * + * @param string $str input string + * @param boolean $utf8 trigger UTF-8 compatibility + * @return boolean + */ + public static function alpha($str, $utf8 = FALSE) + { + $str = (string) $str; + + if ($utf8 === TRUE) + { + return (bool) preg_match('/^\pL++$/uD', $str); + } + else + { + return ctype_alpha($str); + } + } + + /** + * Checks whether a string consists of alphabetical characters and numbers only. + * + * @param string $str input string + * @param boolean $utf8 trigger UTF-8 compatibility + * @return boolean + */ + public static function alpha_numeric($str, $utf8 = FALSE) + { + if ($utf8 === TRUE) + { + return (bool) preg_match('/^[\pL\pN]++$/uD', $str); + } + else + { + return ctype_alnum($str); + } + } + + /** + * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only. + * + * @param string $str input string + * @param boolean $utf8 trigger UTF-8 compatibility + * @return boolean + */ + public static function alpha_dash($str, $utf8 = FALSE) + { + if ($utf8 === TRUE) + { + $regex = '/^[-\pL\pN_]++$/uD'; + } + else + { + $regex = '/^[-a-z0-9_]++$/iD'; + } + + return (bool) preg_match($regex, $str); + } + + /** + * Checks whether a string consists of digits only (no dots or dashes). + * + * @param string $str input string + * @param boolean $utf8 trigger UTF-8 compatibility + * @return boolean + */ + public static function digit($str, $utf8 = FALSE) + { + if ($utf8 === TRUE) + { + return (bool) preg_match('/^\pN++$/uD', $str); + } + else + { + return (is_int($str) AND $str >= 0) OR ctype_digit($str); + } + } + + /** + * Checks whether a string is a valid number (negative and decimal numbers allowed). + * + * Uses {@link http://www.php.net/manual/en/function.localeconv.php locale conversion} + * to allow decimal point to be locale specific. + * + * @param string $str input string + * @return boolean + */ + public static function numeric($str) + { + // Get the decimal point for the current locale + list($decimal) = array_values(localeconv()); + + // A lookahead is used to make sure the string contains at least one digit (before or after the decimal point) + return (bool) preg_match('/^-?+(?=.*[0-9])[0-9]*+'.preg_quote($decimal).'?+[0-9]*+$/D', (string) $str); + } + + /** + * Tests if a number is within a range. + * + * @param string $number number to check + * @param integer $min minimum value + * @param integer $max maximum value + * @param integer $step increment size + * @return boolean + */ + public static function range($number, $min, $max, $step = NULL) + { + if ($number <= $min OR $number >= $max) + { + // Number is outside of range + return FALSE; + } + + if ( ! $step) + { + // Default to steps of 1 + $step = 1; + } + + // Check step requirements + return (($number - $min) % $step === 0); + } + + /** + * Checks if a string is a proper decimal format. Optionally, a specific + * number of digits can be checked too. + * + * @param string $str number to check + * @param integer $places number of decimal places + * @param integer $digits number of digits + * @return boolean + */ + public static function decimal($str, $places = 2, $digits = NULL) + { + if ($digits > 0) + { + // Specific number of digits + $digits = '{'.( (int) $digits).'}'; + } + else + { + // Any number of digits + $digits = '+'; + } + + // Get the decimal point for the current locale + list($decimal) = array_values(localeconv()); + + return (bool) preg_match('/^[+-]?[0-9]'.$digits.preg_quote($decimal).'[0-9]{'.( (int) $places).'}$/D', $str); + } + + /** + * Checks if a string is a proper hexadecimal HTML color value. The validation + * is quite flexible as it does not require an initial "#" and also allows for + * the short notation using only three instead of six hexadecimal characters. + * + * @param string $str input string + * @return boolean + */ + public static function color($str) + { + return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $str); + } + + /** + * Checks if a field matches the value of another field. + * + * @param array $array array of values + * @param string $field field name + * @param string $match field name to match + * @return boolean + */ + public static function matches($array, $field, $match) + { + return ($array[$field] === $array[$match]); + } + +} // End Valid diff --git a/system/classes/Kohana/Validation.php b/system/classes/Kohana/Validation.php new file mode 100644 index 0000000..0934bc4 --- /dev/null +++ b/system/classes/Kohana/Validation.php @@ -0,0 +1,612 @@ + rule + protected $_errors = array(); + + // Array to validate + protected $_data = array(); + + /** + * Sets the unique "any field" key and creates an ArrayObject from the + * passed array. + * + * @param array $array array to validate + * @return void + */ + public function __construct(array $array) + { + $this->_data = $array; + } + + /** + * Throws an exception because Validation is read-only. + * Implements ArrayAccess method. + * + * @throws Kohana_Exception + * @param string $offset key to set + * @param mixed $value value to set + * @return void + */ + public function offsetSet($offset, $value) + { + throw new Kohana_Exception('Validation objects are read-only.'); + } + + /** + * Checks if key is set in array data. + * Implements ArrayAccess method. + * + * @param string $offset key to check + * @return bool whether the key is set + */ + public function offsetExists($offset) + { + return isset($this->_data[$offset]); + } + + /** + * Throws an exception because Validation is read-only. + * Implements ArrayAccess method. + * + * @throws Kohana_Exception + * @param string $offset key to unset + * @return void + */ + public function offsetUnset($offset) + { + throw new Kohana_Exception('Validation objects are read-only.'); + } + + /** + * Gets a value from the array data. + * Implements ArrayAccess method. + * + * @param string $offset key to return + * @return mixed value from array + */ + public function offsetGet($offset) + { + return $this->_data[$offset]; + } + + /** + * Copies the current rules to a new array. + * + * $copy = $array->copy($new_data); + * + * @param array $array new data set + * @return Validation + * @since 3.0.5 + */ + public function copy(array $array) + { + // Create a copy of the current validation set + $copy = clone $this; + + // Replace the data set + $copy->_data = $array; + + return $copy; + } + + /** + * Returns the array representation of the current object. + * Deprecated in favor of [Validation::data] + * + * @deprecated + * @return array + */ + public function as_array() + { + return $this->_data; + } + + /** + * Returns the array of data to be validated. + * + * @return array + */ + public function data() + { + return $this->_data; + } + + /** + * Sets or overwrites the label name for a field. + * + * @param string $field field name + * @param string $label label + * @return $this + */ + public function label($field, $label) + { + // Set the label for this field + $this->_labels[$field] = $label; + + return $this; + } + + /** + * Sets labels using an array. + * + * @param array $labels list of field => label names + * @return $this + */ + public function labels(array $labels) + { + $this->_labels = $labels + $this->_labels; + + return $this; + } + + /** + * Overwrites or appends rules to a field. Each rule will be executed once. + * All rules must be string names of functions method names. Parameters must + * match the parameters of the callback function exactly + * + * Aliases you can use in callback parameters: + * - :validation - the validation object + * - :field - the field name + * - :value - the value of the field + * + * // The "username" must not be empty and have a minimum length of 4 + * $validation->rule('username', 'not_empty') + * ->rule('username', 'min_length', array(':value', 4)); + * + * // The "password" field must match the "password_repeat" field + * $validation->rule('password', 'matches', array(':validation', 'password', 'password_repeat')); + * + * // Using closure (anonymous function) + * $validation->rule('index', + * function(Validation $array, $field, $value) + * { + * if ($value > 6 AND $value < 10) + * { + * $array->error($field, 'custom'); + * } + * } + * , array(':validation', ':field', ':value') + * ); + * + * [!!] Errors must be added manually when using closures! + * + * @param string $field field name + * @param callback $rule valid PHP callback or closure + * @param array $params extra parameters for the rule + * @return $this + */ + public function rule($field, $rule, array $params = NULL) + { + if ($params === NULL) + { + // Default to array(':value') + $params = array(':value'); + } + + if ($field !== TRUE AND ! isset($this->_labels[$field])) + { + // Set the field label to the field name + $this->_labels[$field] = preg_replace('/[^\pL]+/u', ' ', $field); + } + + // Store the rule and params for this rule + $this->_rules[$field][] = array($rule, $params); + + return $this; + } + + /** + * Add rules using an array. + * + * @param string $field field name + * @param array $rules list of callbacks + * @return $this + */ + public function rules($field, array $rules) + { + foreach ($rules as $rule) + { + $this->rule($field, $rule[0], Arr::get($rule, 1)); + } + + return $this; + } + + /** + * Bind a value to a parameter definition. + * + * // This allows you to use :model in the parameter definition of rules + * $validation->bind(':model', $model) + * ->rule('status', 'valid_status', array(':model')); + * + * @param string $key variable name or an array of variables + * @param mixed $value value + * @return $this + */ + public function bind($key, $value = NULL) + { + if (is_array($key)) + { + foreach ($key as $name => $value) + { + $this->_bound[$name] = $value; + } + } + else + { + $this->_bound[$key] = $value; + } + + return $this; + } + + /** + * Executes all validation rules. This should + * typically be called within an if/else block. + * + * if ($validation->check()) + * { + * // The data is valid, do something here + * } + * + * @return boolean + */ + public function check() + { + if (Kohana::$profiling === TRUE) + { + // Start a new benchmark + $benchmark = Profiler::start('Validation', __FUNCTION__); + } + + // New data set + $data = $this->_errors = array(); + + // Store the original data because this class should not modify it post-validation + $original = $this->_data; + + // Get a list of the expected fields + $expected = Arr::merge(array_keys($original), array_keys($this->_labels)); + + // Import the rules locally + $rules = $this->_rules; + + foreach ($expected as $field) + { + // Use the submitted value or NULL if no data exists + $data[$field] = Arr::get($this, $field); + + if (isset($rules[TRUE])) + { + if ( ! isset($rules[$field])) + { + // Initialize the rules for this field + $rules[$field] = array(); + } + + // Append the rules + $rules[$field] = array_merge($rules[$field], $rules[TRUE]); + } + } + + // Overload the current array with the new one + $this->_data = $data; + + // Remove the rules that apply to every field + unset($rules[TRUE]); + + // Bind the validation object to :validation + $this->bind(':validation', $this); + // Bind the data to :data + $this->bind(':data', $this->_data); + + // Execute the rules + foreach ($rules as $field => $set) + { + // Get the field value + $value = $this[$field]; + + // Bind the field name and value to :field and :value respectively + $this->bind(array + ( + ':field' => $field, + ':value' => $value, + )); + + foreach ($set as $array) + { + // Rules are defined as array($rule, $params) + list($rule, $params) = $array; + + foreach ($params as $key => $param) + { + if (is_string($param) AND array_key_exists($param, $this->_bound)) + { + // Replace with bound value + $params[$key] = $this->_bound[$param]; + } + } + + // Default the error name to be the rule (except array and lambda rules) + $error_name = $rule; + + if (is_array($rule)) + { + // Allows rule('field', array(':model', 'some_rule')); + if (is_string($rule[0]) AND array_key_exists($rule[0], $this->_bound)) + { + // Replace with bound value + $rule[0] = $this->_bound[$rule[0]]; + } + + // This is an array callback, the method name is the error name + $error_name = $rule[1]; + $passed = call_user_func_array($rule, $params); + } + elseif ( ! is_string($rule)) + { + // This is a lambda function, there is no error name (errors must be added manually) + $error_name = FALSE; + $passed = call_user_func_array($rule, $params); + } + elseif (method_exists('Valid', $rule)) + { + // Use a method in this object + $method = new ReflectionMethod('Valid', $rule); + + // Call static::$rule($this[$field], $param, ...) with Reflection + $passed = $method->invokeArgs(NULL, $params); + } + elseif (strpos($rule, '::') === FALSE) + { + // Use a function call + $function = new ReflectionFunction($rule); + + // Call $function($this[$field], $param, ...) with Reflection + $passed = $function->invokeArgs($params); + } + else + { + // Split the class and method of the rule + list($class, $method) = explode('::', $rule, 2); + + // Use a static method call + $method = new ReflectionMethod($class, $method); + + // Call $Class::$method($this[$field], $param, ...) with Reflection + $passed = $method->invokeArgs(NULL, $params); + } + + // Ignore return values from rules when the field is empty + if ( ! in_array($rule, $this->_empty_rules) AND ! Valid::not_empty($value)) + continue; + + if ($passed === FALSE AND $error_name !== FALSE) + { + // Add the rule to the errors + $this->error($field, $error_name, $params); + + // This field has an error, stop executing rules + break; + } + elseif (isset($this->_errors[$field])) + { + // The callback added the error manually, stop checking rules + break; + } + } + } + + // Restore the data to its original form + $this->_data = $original; + + if (isset($benchmark)) + { + // Stop benchmarking + Profiler::stop($benchmark); + } + + return empty($this->_errors); + } + + /** + * Add an error to a field. + * + * @param string $field field name + * @param string $error error message + * @param array $params + * @return $this + */ + public function error($field, $error, array $params = NULL) + { + $this->_errors[$field] = array($error, $params); + + return $this; + } + + /** + * Returns the error messages. If no file is specified, the error message + * will be the name of the rule that failed. When a file is specified, the + * message will be loaded from "field/rule", or if no rule-specific message + * exists, "field/default" will be used. If neither is set, the returned + * message will be "file/field/rule". + * + * By default all messages are translated using the default language. + * A string can be used as the second parameter to specified the language + * that the message was written in. + * + * // Get errors from messages/forms/login.php + * $errors = $Validation->errors('forms/login'); + * + * @uses Kohana::message + * @param string $file file to load error messages from + * @param mixed $translate translate the message + * @return array + */ + public function errors($file = NULL, $translate = TRUE) + { + if ($file === NULL) + { + // Return the error list + return $this->_errors; + } + + // Create a new message list + $messages = array(); + + foreach ($this->_errors as $field => $set) + { + list($error, $params) = $set; + + // Get the label for this field + $label = $this->_labels[$field]; + + if ($translate) + { + if (is_string($translate)) + { + // Translate the label using the specified language + $label = __($label, NULL, $translate); + } + else + { + // Translate the label + $label = __($label); + } + } + + // Start the translation values list + $values = array( + ':field' => $label, + ':value' => Arr::get($this, $field), + ); + + if (is_array($values[':value'])) + { + // All values must be strings + $values[':value'] = implode(', ', Arr::flatten($values[':value'])); + } + + if ($params) + { + foreach ($params as $key => $value) + { + if (is_array($value)) + { + // All values must be strings + $value = implode(', ', Arr::flatten($value)); + } + elseif (is_object($value)) + { + // Objects cannot be used in message files + continue; + } + + // Check if a label for this parameter exists + if (isset($this->_labels[$value])) + { + // Use the label as the value, eg: related field name for "matches" + $value = $this->_labels[$value]; + + if ($translate) + { + if (is_string($translate)) + { + // Translate the value using the specified language + $value = __($value, NULL, $translate); + } + else + { + // Translate the value + $value = __($value); + } + } + } + + // Add each parameter as a numbered value, starting from 1 + $values[':param'.($key + 1)] = $value; + } + } + + if ($message = Kohana::message($file, "{$field}.{$error}") AND is_string($message)) + { + // Found a message for this field and error + } + elseif ($message = Kohana::message($file, "{$field}.default") AND is_string($message)) + { + // Found a default message for this field + } + elseif ($message = Kohana::message($file, $error) AND is_string($message)) + { + // Found a default message for this error + } + elseif ($message = Kohana::message('validation', $error) AND is_string($message)) + { + // Found a default message for this error + } + else + { + // No message exists, display the path expected + $message = "{$file}.{$field}.{$error}"; + } + + if ($translate) + { + if (is_string($translate)) + { + // Translate the message using specified language + $message = __($message, $values, $translate); + } + else + { + // Translate the message using the default language + $message = __($message, $values); + } + } + else + { + // Do not translate, just replace the values + $message = strtr($message, $values); + } + + // Set the message for this field + $messages[$field] = $message; + } + + return $messages; + } + +} // End Validation diff --git a/system/classes/Kohana/Validation/Exception.php b/system/classes/Kohana/Validation/Exception.php new file mode 100644 index 0000000..9a60c67 --- /dev/null +++ b/system/classes/Kohana/Validation/Exception.php @@ -0,0 +1,29 @@ +array = $array; + + parent::__construct($message, $values, $code, $previous); + } + +} // End Kohana_Validation_Exception diff --git a/system/classes/Kohana/View.php b/system/classes/Kohana/View.php new file mode 100644 index 0000000..cb0b5b5 --- /dev/null +++ b/system/classes/Kohana/View.php @@ -0,0 +1,351 @@ + $value) + { + View::$_global_data[$key2] = $value; + } + } + else + { + View::$_global_data[$key] = $value; + } + } + + /** + * Assigns a global variable by reference, similar to [View::bind], except + * that the variable will be accessible to all views. + * + * View::bind_global($key, $value); + * + * @param string $key variable name + * @param mixed $value referenced variable + * @return void + */ + public static function bind_global($key, & $value) + { + View::$_global_data[$key] =& $value; + } + + // View filename + protected $_file; + + // Array of local variables + protected $_data = array(); + + /** + * Sets the initial view filename and local data. Views should almost + * always only be created using [View::factory]. + * + * $view = new View($file); + * + * @param string $file view filename + * @param array $data array of values + * @return void + * @uses View::set_filename + */ + public function __construct($file = NULL, array $data = NULL) + { + if ($file !== NULL) + { + $this->set_filename($file); + } + + if ($data !== NULL) + { + // Add the values to the current data + $this->_data = $data + $this->_data; + } + } + + /** + * Magic method, searches for the given variable and returns its value. + * Local variables will be returned before global variables. + * + * $value = $view->foo; + * + * [!!] If the variable has not yet been set, an exception will be thrown. + * + * @param string $key variable name + * @return mixed + * @throws Kohana_Exception + */ + public function & __get($key) + { + if (array_key_exists($key, $this->_data)) + { + return $this->_data[$key]; + } + elseif (array_key_exists($key, View::$_global_data)) + { + return View::$_global_data[$key]; + } + else + { + throw new Kohana_Exception('View variable is not set: :var', + array(':var' => $key)); + } + } + + /** + * Magic method, calls [View::set] with the same parameters. + * + * $view->foo = 'something'; + * + * @param string $key variable name + * @param mixed $value value + * @return void + */ + public function __set($key, $value) + { + $this->set($key, $value); + } + + /** + * Magic method, determines if a variable is set. + * + * isset($view->foo); + * + * [!!] `NULL` variables are not considered to be set by [isset](http://php.net/isset). + * + * @param string $key variable name + * @return boolean + */ + public function __isset($key) + { + return (isset($this->_data[$key]) OR isset(View::$_global_data[$key])); + } + + /** + * Magic method, unsets a given variable. + * + * unset($view->foo); + * + * @param string $key variable name + * @return void + */ + public function __unset($key) + { + unset($this->_data[$key], View::$_global_data[$key]); + } + + /** + * Magic method, returns the output of [View::render]. + * + * @return string + * @uses View::render + */ + public function __toString() + { + try + { + return $this->render(); + } + catch (Exception $e) + { + /** + * Display the exception message. + * + * We use this method here because it's impossible to throw and + * exception from __toString(). + */ + $error_response = Kohana_exception::_handler($e); + + return $error_response->body(); + } + } + + /** + * Sets the view filename. + * + * $view->set_filename($file); + * + * @param string $file view filename + * @return View + * @throws View_Exception + */ + public function set_filename($file) + { + if (($path = Kohana::find_file('views', $file)) === FALSE) + { + throw new View_Exception('The requested view :file could not be found', array( + ':file' => $file, + )); + } + + // Store the file path locally + $this->_file = $path; + + return $this; + } + + /** + * Assigns a variable by name. Assigned values will be available as a + * variable within the view file: + * + * // This value can be accessed as $foo within the view + * $view->set('foo', 'my value'); + * + * You can also use an array to set several values at once: + * + * // Create the values $food and $beverage in the view + * $view->set(array('food' => 'bread', 'beverage' => 'water')); + * + * @param string $key variable name or an array of variables + * @param mixed $value value + * @return $this + */ + public function set($key, $value = NULL) + { + if (is_array($key)) + { + foreach ($key as $name => $value) + { + $this->_data[$name] = $value; + } + } + else + { + $this->_data[$key] = $value; + } + + return $this; + } + + /** + * Assigns a value by reference. The benefit of binding is that values can + * be altered without re-setting them. It is also possible to bind variables + * before they have values. Assigned values will be available as a + * variable within the view file: + * + * // This reference can be accessed as $ref within the view + * $view->bind('ref', $bar); + * + * @param string $key variable name + * @param mixed $value referenced variable + * @return $this + */ + public function bind($key, & $value) + { + $this->_data[$key] =& $value; + + return $this; + } + + /** + * Renders the view object to a string. Global and local data are merged + * and extracted to create local variables within the view file. + * + * $output = $view->render(); + * + * [!!] Global variables with the same key name as local variables will be + * overwritten by the local variable. + * + * @param string $file view filename + * @return string + * @throws View_Exception + * @uses View::capture + */ + public function render($file = NULL) + { + if ($file !== NULL) + { + $this->set_filename($file); + } + + if (empty($this->_file)) + { + throw new View_Exception('You must set the file to use within your view before rendering'); + } + + // Combine local and global data and capture the output + return View::capture($this->_file, $this->_data); + } + +} // End View diff --git a/system/classes/Kohana/View/Exception.php b/system/classes/Kohana/View/Exception.php new file mode 100644 index 0000000..536c550 --- /dev/null +++ b/system/classes/Kohana/View/Exception.php @@ -0,0 +1,9 @@ + array( + 'length' => '13,14,15,16,17,18,19', + 'prefix' => '', + 'luhn' => TRUE, + ), + + 'american express' => array( + 'length' => '15', + 'prefix' => '3[47]', + 'luhn' => TRUE, + ), + + 'diners club' => array( + 'length' => '14,16', + 'prefix' => '36|55|30[0-5]', + 'luhn' => TRUE, + ), + + 'discover' => array( + 'length' => '16', + 'prefix' => '6(?:5|011)', + 'luhn' => TRUE, + ), + + 'jcb' => array( + 'length' => '15,16', + 'prefix' => '3|1800|2131', + 'luhn' => TRUE, + ), + + 'maestro' => array( + 'length' => '16,18', + 'prefix' => '50(?:20|38)|6(?:304|759)', + 'luhn' => TRUE, + ), + + 'mastercard' => array( + 'length' => '16', + 'prefix' => '5[1-5]', + 'luhn' => TRUE, + ), + + 'visa' => array( + 'length' => '13,16', + 'prefix' => '4', + 'luhn' => TRUE, + ), + +); \ No newline at end of file diff --git a/system/config/curl.php b/system/config/curl.php new file mode 100644 index 0000000..ae015b5 --- /dev/null +++ b/system/config/curl.php @@ -0,0 +1,8 @@ + 'Mozilla/5.0 (compatible; Kohana v'.Kohana::VERSION.' +http://kohanaframework.org/)', + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 5, + CURLOPT_HEADER => FALSE, +); \ No newline at end of file diff --git a/system/config/encrypt.php b/system/config/encrypt.php new file mode 100644 index 0000000..3f9065e --- /dev/null +++ b/system/config/encrypt.php @@ -0,0 +1,17 @@ + array( + /** + * The following options must be set: + * + * string key secret passphrase + * integer mode encryption mode, one of MCRYPT_MODE_* + * integer cipher encryption cipher, one of the Mcrpyt cipher constants + */ + 'cipher' => MCRYPT_RIJNDAEL_128, + 'mode' => MCRYPT_MODE_NOFB, + ), + +); diff --git a/system/config/inflector.php b/system/config/inflector.php new file mode 100644 index 0000000..ea28db1 --- /dev/null +++ b/system/config/inflector.php @@ -0,0 +1,98 @@ + array( + 'access', + 'advice', + 'aircraft', + 'art', + 'baggage', + 'bison', + 'dances', + 'deer', + 'equipment', + 'fish', + 'fuel', + 'furniture', + 'heat', + 'honey', + 'homework', + 'impatience', + 'information', + 'knowledge', + 'luggage', + 'media', + 'money', + 'moose', + 'music', + 'news', + 'patience', + 'progress', + 'pollution', + 'research', + 'rice', + 'salmon', + 'sand', + 'series', + 'sheep', + 'sms', + 'spam', + 'species', + 'staff', + 'swine', + 'toothpaste', + 'traffic', + 'understanding', + 'water', + 'weather', + 'work', + ), + + 'irregular' => array( + 'appendix' => 'appendices', + 'cactus' => 'cacti', + 'calf' => 'calves', + 'child' => 'children', + 'crisis' => 'crises', + 'criterion' => 'criteria', + 'curriculum' => 'curricula', + 'diagnosis' => 'diagnoses', + 'elf' => 'elves', + 'ellipsis' => 'ellipses', + 'foot' => 'feet', + 'goose' => 'geese', + 'hero' => 'heroes', + 'hoof' => 'hooves', + 'hypothesis' => 'hypotheses', + 'is' => 'are', + 'knife' => 'knives', + 'leaf' => 'leaves', + 'life' => 'lives', + 'loaf' => 'loaves', + 'man' => 'men', + 'mouse' => 'mice', + 'nucleus' => 'nuclei', + 'oasis' => 'oases', + 'octopus' => 'octopi', + 'ox' => 'oxen', + 'paralysis' => 'paralyses', + 'parenthesis' => 'parentheses', + 'person' => 'people', + 'phenomenon' => 'phenomena', + 'potato' => 'potatoes', + 'quiz' => 'quizzes', + 'radius' => 'radii', + 'scarf' => 'scarves', + 'stimulus' => 'stimuli', + 'syllabus' => 'syllabi', + 'synthesis' => 'syntheses', + 'thief' => 'thieves', + 'tooth' => 'teeth', + 'was' => 'were', + 'wharf' => 'wharves', + 'wife' => 'wives', + 'woman' => 'women', + 'release' => 'releases', + ), +); diff --git a/system/config/mimes.php b/system/config/mimes.php new file mode 100644 index 0000000..70084f3 --- /dev/null +++ b/system/config/mimes.php @@ -0,0 +1,227 @@ + array('text/h323'), + '7z' => array('application/x-7z-compressed'), + 'abw' => array('application/x-abiword'), + 'acx' => array('application/internet-property-stream'), + 'ai' => array('application/postscript'), + 'aif' => array('audio/x-aiff'), + 'aifc' => array('audio/x-aiff'), + 'aiff' => array('audio/x-aiff'), + 'amf' => array('application/x-amf'), + 'asf' => array('video/x-ms-asf'), + 'asr' => array('video/x-ms-asf'), + 'asx' => array('video/x-ms-asf'), + 'atom' => array('application/atom+xml'), + 'avi' => array('video/avi', 'video/msvideo', 'video/x-msvideo'), + 'bin' => array('application/octet-stream','application/macbinary'), + 'bmp' => array('image/bmp'), + 'c' => array('text/x-csrc'), + 'c++' => array('text/x-c++src'), + 'cab' => array('application/x-cab'), + 'cc' => array('text/x-c++src'), + 'cda' => array('application/x-cdf'), + 'class' => array('application/octet-stream'), + 'cpp' => array('text/x-c++src'), + 'cpt' => array('application/mac-compactpro'), + 'csh' => array('text/x-csh'), + 'css' => array('text/css'), + 'csv' => array('text/x-comma-separated-values', 'application/vnd.ms-excel', 'text/comma-separated-values', 'text/csv'), + 'dbk' => array('application/docbook+xml'), + 'dcr' => array('application/x-director'), + 'deb' => array('application/x-debian-package'), + 'diff' => array('text/x-diff'), + 'dir' => array('application/x-director'), + 'divx' => array('video/divx'), + 'dll' => array('application/octet-stream', 'application/x-msdos-program'), + 'dmg' => array('application/x-apple-diskimage'), + 'dms' => array('application/octet-stream'), + 'doc' => array('application/msword'), + 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document'), + 'dvi' => array('application/x-dvi'), + 'dxr' => array('application/x-director'), + 'eml' => array('message/rfc822'), + 'eps' => array('application/postscript'), + 'evy' => array('application/envoy'), + 'exe' => array('application/x-msdos-program', 'application/octet-stream'), + 'fla' => array('application/octet-stream'), + 'flac' => array('application/x-flac'), + 'flc' => array('video/flc'), + 'fli' => array('video/fli'), + 'flv' => array('video/x-flv'), + 'gif' => array('image/gif'), + 'gtar' => array('application/x-gtar'), + 'gz' => array('application/x-gzip'), + 'h' => array('text/x-chdr'), + 'h++' => array('text/x-c++hdr'), + 'hh' => array('text/x-c++hdr'), + 'hpp' => array('text/x-c++hdr'), + 'hqx' => array('application/mac-binhex40'), + 'hs' => array('text/x-haskell'), + 'htm' => array('text/html'), + 'html' => array('text/html'), + 'ico' => array('image/x-icon'), + 'ics' => array('text/calendar'), + 'iii' => array('application/x-iphone'), + 'ins' => array('application/x-internet-signup'), + 'iso' => array('application/x-iso9660-image'), + 'isp' => array('application/x-internet-signup'), + 'jar' => array('application/java-archive'), + 'java' => array('application/x-java-applet'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'js' => array('application/javascript'), + 'json' => array('application/json'), + 'latex' => array('application/x-latex'), + 'lha' => array('application/octet-stream'), + 'log' => array('text/plain', 'text/x-log'), + 'lzh' => array('application/octet-stream'), + 'm4a' => array('audio/mpeg'), + 'm4p' => array('video/mp4v-es'), + 'm4v' => array('video/mp4'), + 'man' => array('application/x-troff-man'), + 'mdb' => array('application/x-msaccess'), + 'midi' => array('audio/midi'), + 'mid' => array('audio/midi'), + 'mif' => array('application/vnd.mif'), + 'mka' => array('audio/x-matroska'), + 'mkv' => array('video/x-matroska'), + 'mov' => array('video/quicktime'), + 'movie' => array('video/x-sgi-movie'), + 'mp2' => array('audio/mpeg'), + 'mp3' => array('audio/mpeg'), + 'mp4' => array('application/mp4','audio/mp4','video/mp4'), + 'mpa' => array('video/mpeg'), + 'mpe' => array('video/mpeg'), + 'mpeg' => array('video/mpeg'), + 'mpg' => array('video/mpeg'), + 'mpg4' => array('video/mp4'), + 'mpga' => array('audio/mpeg'), + 'mpp' => array('application/vnd.ms-project'), + 'mpv' => array('video/x-matroska'), + 'mpv2' => array('video/mpeg'), + 'ms' => array('application/x-troff-ms'), + 'msg' => array('application/msoutlook','application/x-msg'), + 'msi' => array('application/x-msi'), + 'nws' => array('message/rfc822'), + 'oda' => array('application/oda'), + 'odb' => array('application/vnd.oasis.opendocument.database'), + 'odc' => array('application/vnd.oasis.opendocument.chart'), + 'odf' => array('application/vnd.oasis.opendocument.forumla'), + 'odg' => array('application/vnd.oasis.opendocument.graphics'), + 'odi' => array('application/vnd.oasis.opendocument.image'), + 'odm' => array('application/vnd.oasis.opendocument.text-master'), + 'odp' => array('application/vnd.oasis.opendocument.presentation'), + 'ods' => array('application/vnd.oasis.opendocument.spreadsheet'), + 'odt' => array('application/vnd.oasis.opendocument.text'), + 'oga' => array('audio/ogg'), + 'ogg' => array('application/ogg'), + 'ogv' => array('video/ogg'), + 'otg' => array('application/vnd.oasis.opendocument.graphics-template'), + 'oth' => array('application/vnd.oasis.opendocument.web'), + 'otp' => array('application/vnd.oasis.opendocument.presentation-template'), + 'ots' => array('application/vnd.oasis.opendocument.spreadsheet-template'), + 'ott' => array('application/vnd.oasis.opendocument.template'), + 'p' => array('text/x-pascal'), + 'pas' => array('text/x-pascal'), + 'patch' => array('text/x-diff'), + 'pbm' => array('image/x-portable-bitmap'), + 'pdf' => array('application/pdf', 'application/x-download'), + 'php' => array('application/x-httpd-php'), + 'php3' => array('application/x-httpd-php'), + 'php4' => array('application/x-httpd-php'), + 'php5' => array('application/x-httpd-php'), + 'phps' => array('application/x-httpd-php-source'), + 'phtml' => array('application/x-httpd-php'), + 'pl' => array('text/x-perl'), + 'pm' => array('text/x-perl'), + 'png' => array('image/png', 'image/x-png'), + 'po' => array('text/x-gettext-translation'), + 'pot' => array('application/vnd.ms-powerpoint'), + 'pps' => array('application/vnd.ms-powerpoint'), + 'ppt' => array('application/powerpoint'), + 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation'), + 'ps' => array('application/postscript'), + 'psd' => array('application/x-photoshop', 'image/x-photoshop'), + 'pub' => array('application/x-mspublisher'), + 'py' => array('text/x-python'), + 'qt' => array('video/quicktime'), + 'ra' => array('audio/x-realaudio'), + 'ram' => array('audio/x-realaudio', 'audio/x-pn-realaudio'), + 'rar' => array('application/rar'), + 'rgb' => array('image/x-rgb'), + 'rm' => array('audio/x-pn-realaudio'), + 'rpm' => array('audio/x-pn-realaudio-plugin', 'application/x-redhat-package-manager'), + 'rss' => array('application/rss+xml'), + 'rtf' => array('text/rtf'), + 'rtx' => array('text/richtext'), + 'rv' => array('video/vnd.rn-realvideo'), + 'sea' => array('application/octet-stream'), + 'sh' => array('text/x-sh'), + 'shtml' => array('text/html'), + 'sit' => array('application/x-stuffit'), + 'smi' => array('application/smil'), + 'smil' => array('application/smil'), + 'so' => array('application/octet-stream'), + 'src' => array('application/x-wais-source'), + 'svg' => array('image/svg+xml'), + 'swf' => array('application/x-shockwave-flash'), + 't' => array('application/x-troff'), + 'tar' => array('application/x-tar'), + 'tcl' => array('text/x-tcl'), + 'tex' => array('application/x-tex'), + 'text' => array('text/plain'), + 'texti' => array('application/x-texinfo'), + 'textinfo' => array('application/x-texinfo'), + 'tgz' => array('application/x-tar'), + 'tif' => array('image/tiff'), + 'tiff' => array('image/tiff'), + 'torrent' => array('application/x-bittorrent'), + 'tr' => array('application/x-troff'), + 'tsv' => array('text/tab-separated-values'), + 'txt' => array('text/plain'), + 'wav' => array('audio/x-wav'), + 'wax' => array('audio/x-ms-wax'), + 'wbxml' => array('application/wbxml'), + 'webm' => array('video/webm'), + 'wm' => array('video/x-ms-wm'), + 'wma' => array('audio/x-ms-wma'), + 'wmd' => array('application/x-ms-wmd'), + 'wmlc' => array('application/wmlc'), + 'wmv' => array('video/x-ms-wmv', 'application/octet-stream'), + 'wmx' => array('video/x-ms-wmx'), + 'wmz' => array('application/x-ms-wmz'), + 'word' => array('application/msword', 'application/octet-stream'), + 'wp5' => array('application/wordperfect5.1'), + 'wpd' => array('application/vnd.wordperfect'), + 'wvx' => array('video/x-ms-wvx'), + 'xbm' => array('image/x-xbitmap'), + 'xcf' => array('image/xcf'), + 'xhtml' => array('application/xhtml+xml'), + 'xht' => array('application/xhtml+xml'), + 'xl' => array('application/excel', 'application/vnd.ms-excel'), + 'xla' => array('application/excel', 'application/vnd.ms-excel'), + 'xlc' => array('application/excel', 'application/vnd.ms-excel'), + 'xlm' => array('application/excel', 'application/vnd.ms-excel'), + 'xls' => array('application/excel', 'application/vnd.ms-excel'), + 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'), + 'xlt' => array('application/excel', 'application/vnd.ms-excel'), + 'xml' => array('text/xml', 'application/xml'), + 'xof' => array('x-world/x-vrml'), + 'xpm' => array('image/x-xpixmap'), + 'xsl' => array('text/xml'), + 'xvid' => array('video/x-xvid'), + 'xwd' => array('image/x-xwindowdump'), + 'z' => array('application/x-compress'), + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed') +); diff --git a/system/config/session.php b/system/config/session.php new file mode 100644 index 0000000..c130044 --- /dev/null +++ b/system/config/session.php @@ -0,0 +1,7 @@ + array( + 'encrypted' => FALSE, + ), +); diff --git a/system/config/user_agents.php b/system/config/user_agents.php new file mode 100644 index 0000000..7960f5e --- /dev/null +++ b/system/config/user_agents.php @@ -0,0 +1,108 @@ + array( + 'windows nt 6.2' => 'Windows 8', + 'windows nt 6.1' => 'Windows 7', + 'windows nt 6.0' => 'Windows Vista', + 'windows nt 5.2' => 'Windows 2003', + 'windows nt 5.1' => 'Windows XP', + 'windows nt 5.0' => 'Windows 2000', + 'windows nt 4.0' => 'Windows NT', + 'winnt4.0' => 'Windows NT', + 'winnt 4.0' => 'Windows NT', + 'winnt' => 'Windows NT', + 'windows 98' => 'Windows 98', + 'win98' => 'Windows 98', + 'windows 95' => 'Windows 95', + 'win95' => 'Windows 95', + 'windows' => 'Unknown Windows OS', + 'os x' => 'Mac OS X', + 'intel mac' => 'Intel Mac', + 'ppc mac' => 'PowerPC Mac', + 'powerpc' => 'PowerPC', + 'ppc' => 'PowerPC', + 'cygwin' => 'Cygwin', + 'linux' => 'Linux', + 'debian' => 'Debian', + 'openvms' => 'OpenVMS', + 'sunos' => 'Sun Solaris', + 'amiga' => 'Amiga', + 'beos' => 'BeOS', + 'apachebench' => 'ApacheBench', + 'freebsd' => 'FreeBSD', + 'netbsd' => 'NetBSD', + 'bsdi' => 'BSDi', + 'openbsd' => 'OpenBSD', + 'os/2' => 'OS/2', + 'warp' => 'OS/2', + 'aix' => 'AIX', + 'irix' => 'Irix', + 'osf' => 'DEC OSF', + 'hp-ux' => 'HP-UX', + 'hurd' => 'GNU/Hurd', + 'unix' => 'Unknown Unix OS', + ), + + 'browser' => array( + 'Opera' => 'Opera', + 'MSIE' => 'Internet Explorer', + 'Internet Explorer' => 'Internet Explorer', + 'Shiira' => 'Shiira', + 'Firefox' => 'Firefox', + 'Chimera' => 'Chimera', + 'Phoenix' => 'Phoenix', + 'Firebird' => 'Firebird', + 'Camino' => 'Camino', + 'Navigator' => 'Netscape', + 'Netscape' => 'Netscape', + 'OmniWeb' => 'OmniWeb', + 'Chrome' => 'Chrome', + 'Safari' => 'Safari', + 'CFNetwork' => 'Safari', // Core Foundation for OSX, WebKit/Safari + 'Konqueror' => 'Konqueror', + 'Epiphany' => 'Epiphany', + 'Galeon' => 'Galeon', + 'Mozilla' => 'Mozilla', + 'icab' => 'iCab', + 'lynx' => 'Lynx', + 'links' => 'Links', + 'hotjava' => 'HotJava', + 'amaya' => 'Amaya', + 'IBrowse' => 'IBrowse', + ), + + 'mobile' => array( + 'mobileexplorer' => 'Mobile Explorer', + 'openwave' => 'Open Wave', + 'opera mini' => 'Opera Mini', + 'operamini' => 'Opera Mini', + 'elaine' => 'Palm', + 'palmsource' => 'Palm', + 'digital paths' => 'Palm', + 'avantgo' => 'Avantgo', + 'xiino' => 'Xiino', + 'palmscape' => 'Palmscape', + 'nokia' => 'Nokia', + 'ericsson' => 'Ericsson', + 'blackBerry' => 'BlackBerry', + 'motorola' => 'Motorola', + 'iphone' => 'iPhone', + 'ipad' => 'iPad', + 'ipod' => 'iPod', + 'android' => 'Android', + ), + + 'robot' => array( + 'googlebot' => 'Googlebot', + 'msnbot' => 'MSNBot', + 'facebookexternalhit' => 'Facebook', + 'slurp' => 'Inktomi Slurp', + 'yahoo' => 'Yahoo', + 'askjeeves' => 'AskJeeves', + 'fastcrawler' => 'FastCrawler', + 'infoseek' => 'InfoSeek Robot 1.0', + 'lycos' => 'Lycos', + ), +); diff --git a/system/config/userguide.php b/system/config/userguide.php new file mode 100644 index 0000000..b240d7a --- /dev/null +++ b/system/config/userguide.php @@ -0,0 +1,23 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'kohana' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'Kohana', + + // A short description of this module, shown on the index page + 'description' => 'Documentation for Kohana core/system.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2012 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/system/guide/kohana/autoloading.md b/system/guide/kohana/autoloading.md new file mode 100644 index 0000000..49986d7 --- /dev/null +++ b/system/guide/kohana/autoloading.md @@ -0,0 +1,72 @@ +# Loading Classes + +Kohana supports the [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) autoloading specification as of version 3.3. This allows you to take advantage of PHP [autoloading](http://php.net/manual/language.oop5.autoload.php), removing the need to call [include](http://php.net/include) or [require](http://php.net/require) before using a class. When you use a class Kohana will find and include the class file for you. For instance, when you want to use the [Cookie::set] method, you simply call: + + Cookie::set('mycookie', 'any string value'); + +Or to load an [Encrypt] instance, just call [Encrypt::instance]: + + $encrypt = Encrypt::instance(); + +Classes are loaded via the [Kohana::auto_load] method, which makes a simple conversion from class name to file name: + +1. Classes are placed in the `classes/` directory of the [filesystem](files) +2. Any underscore characters in the class name are converted to slashes +2. The filename must match the case of the class + +When calling a class that has not been loaded (eg: `Session_Cookie`), Kohana will search the filesystem using [Kohana::find_file] for a file named `classes/Session/Cookie.php`. + +If your classes do not follow this convention, they cannot be autoloaded by Kohana. You will have to manually included your files, or add your own [autoload function.](http://us3.php.net/manual/en/function.spl-autoload-register.php) + +## Custom Autoloaders + +Kohana's default autoloader is enabled in `application/bootstrap.php` using [spl_autoload_register](http://php.net/spl_autoload_register): + + spl_autoload_register(array('Kohana', 'auto_load')); + +This allows [Kohana::auto_load] to attempt to find and include any class that does not yet exist when the class is first used as long as it follows the PSR-0 specification. If you wish to support the previous Kohana filename convention (using lowercase filesnames), an additional autoloader is provided by Kohana: + + spl_autoload_register(array('Kohana', 'auto_load_lowercase')); + + +### Example: Zend + +You can easily gain access to other libraries if they include an autoloader. For example, here is how to enable Zend's autoloader so you can use Zend libraries in your Kohana application. + +#### Download and install the Zend Framework files + +- [Download the latest Zend Framework files](http://framework.zend.com/download/latest). +- Create a `vendor` directory at `application/vendor`. This keeps third party software separate from your application classes. +- Move the decompressed Zend folder containing Zend Framework to `application/vendor/Zend`. + + +#### Include Zend's Autoloader in your bootstrap + +Somewhere in `application/bootstrap.php`, copy the following code: + + /** + * Enable Zend Framework autoloading + */ + if ($path = Kohana::find_file('vendor', 'Zend/Loader')) + { + ini_set('include_path', + ini_get('include_path').PATH_SEPARATOR.dirname(dirname($path))); + + require_once 'Zend/Loader/Autoloader.php'; + Zend_Loader_Autoloader::getInstance(); + } + +#### Usage example + +You can now autoload any Zend Framework classes from inside your Kohana application. + + if ($validate($this->request->post())) + { + $mailer = new Zend_Mail; + + $mailer->setBodyHtml($view) + ->setFrom(Kohana::$config->load('site')->email_from) + ->addTo($email) + ->setSubject($message) + ->send(); + } diff --git a/system/guide/kohana/bootstrap.md b/system/guide/kohana/bootstrap.md new file mode 100644 index 0000000..22c560f --- /dev/null +++ b/system/guide/kohana/bootstrap.md @@ -0,0 +1,111 @@ +# Bootstrap + +The bootstrap is located at `application/bootstrap.php`. It is responsible for setting up the Kohana environment and executing the main response. It is included by `index.php` (see [Request flow](flow)) + +[!!] The bootstrap is responsible for the flow of your application. In previous versions of Kohana the bootstrap was in `system` and was somewhat of an unseen, uneditible force. In Kohana 3 the bootstrap takes on a much more integral and versatile role. Do not be afraid to edit and change your bootstrap however you see fit. + +## Environment setup + +The bootstrap first sets the timezone and locale, and then adds Kohana's autoloader so the [cascading filesystem](files) works. You could add any other settings that all your application needed here. + +~~~ +// Sample excerpt from bootstrap.php with comments trimmed down + +// Set the default time zone. +date_default_timezone_set('America/Chicago'); + +// Set the default locale. +setlocale(LC_ALL, 'en_US.utf-8'); + +// Enable the Kohana auto-loader. +spl_autoload_register(array('Kohana', 'auto_load')); + +// Enable the Kohana auto-loader for unserialization. +ini_set('unserialize_callback_func', 'spl_autoload_call'); +~~~ + +## Initialization and Configuration + +Kohana is then initialized by calling [Kohana::init], and the log and [config](files/config) reader/writers are enabled. + +~~~ +// Sample excerpt from bootstrap.php with comments trimmed down + +Kohana::init(array(' + base_url' => '/kohana/', + index_file => false, +)); + +// Attach the file writer to logging. Multiple writers are supported. +Kohana::$log->attach(new Kohana_Log_File(APPPATH.'logs')); + +// Attach a file reader to config. Multiple readers are supported. +Kohana::$config->attach(new Kohana_Config_File); +~~~ + +You can add conditional statements to make the bootstrap have different values based on certain settings. For example, detect whether we are live by checking `$_SERVER['HTTP_HOST']` and set caching, profiling, etc. accordingly. This is just an example, there are many different ways to accomplish the same thing. + +~~~ +// Excerpt from http://github.com/isaiahdw/kohanaphp.com/blob/f2afe8e28b/application/bootstrap.php +... [trimmed] + +/** + * Set the environment status by the domain. + */ +if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE) +{ + // We are live! + Kohana::$environment = Kohana::PRODUCTION; + + // Turn off notices and strict errors + error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT); +} + +/** + * Initialize Kohana, setting the default options. + ... [trimmed] + */ +Kohana::init(array( + 'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaphp.com/', + 'caching' => Kohana::$environment === Kohana::PRODUCTION, + 'profile' => Kohana::$environment !== Kohana::PRODUCTION, + 'index_file' => FALSE, +)); + +... [trimmed] + +~~~ + +[!!] Note: The default bootstrap will set `Kohana::$environment = $_ENV['KOHANA_ENV']` if set. Docs on how to supply this variable are available in your web server's documentation (e.g. [Apache](http://httpd.apache.org/docs/1.3/mod/mod_env.html#setenv), [Lighttpd](http://redmine.lighttpd.net/wiki/1/Docs:ModSetEnv#Options)). This is considered better practice than many alternative methods to set `Kohana::$enviroment`, as you can change the setting per server, without having to rely on config options or hostnames. + +## Modules + +**Read the [Modules](modules) page for a more detailed description.** + +[Modules](modules) are then loaded using [Kohana::modules()]. Including modules is optional. + +Each key in the array should be the name of the module, and the value is the path to the module, either relative or absolute. +~~~ +// Example excerpt from bootstrap.php + +Kohana::modules(array( + 'database' => MODPATH.'database', + 'orm' => MODPATH.'orm', + 'userguide' => MODPATH.'userguide', +)); +~~~ + +## Routes + +**Read the [Routing](routing) page for a more detailed description and more examples.** + +[Routes](routing) are then defined via [Route::set()]. + +~~~ +// The default route that comes with Kohana 3 +Route::set('default', '((/(/)))') + ->defaults(array( + 'controller' => 'Welcome', + 'action' => 'index', + )); +~~~ diff --git a/system/guide/kohana/config.md b/system/guide/kohana/config.md new file mode 100644 index 0000000..1673f96 --- /dev/null +++ b/system/guide/kohana/config.md @@ -0,0 +1,193 @@ +# Configuration + +By default Kohana is setup to load configuration values from [config files](files/config) in the +cascading filesystem. However, it is very easy to adapt it to load config values in other +locations/formats. + +## Sources + +The system is designed around the concept of **Config Sources**, which loosely means a method of +storing configuration values. + +To read config from a source you need a **Config Reader**. Similarly, to write config to a source +you need a **Config Writer**. + +Implementing them is as simple as extending the +[Kohana_Config_Reader] / [Kohana_Config_Writer] interfaces: + + class Kohana_Config_Database_Reader implements Kohana_Config_Reader + class Kohana_Config_Database_Writer extends Kohana_Config_Database_Reader implements Kohana_Config_Writer + +You'll notice in the above example that the Database Writer extends the Database Reader. +This is the convention with config sources, the reasoning being that if you can write to a +source chances are you can also read from it as well. However, this convention is not enforced +and is left to the developer's discretion. + +## Groups + +In order to aid organisation config values are split up into logical "groups". For example, +database related settings go in a `database` group, and session related settings go in a +`session` group. + +How these groups are stored/organised is up to the config source. For example, the file source +puts different config groups into different files (`database.php`, `session.php`) whereas +the database source uses a column to distinguish between groups. + +To load a config group simply call `Kohana::$config->load()` with the name of the group you wish to load: + + $config = Kohana::$config->load('my_group'); + +`load()` will return an instance of [Config_Group] which encapsulates the config values and ensures +that any modifications made will be passed back to the config writers. + +To get a config value from a [Config_Group] object simply call [Config_Group::get]: + + $config = Kohana::$config->load('my_group'); + $value = $config->get('var'); + +To modify a value call [Config_Group::set]: + + $config = Kohana::$config->load('my_group'); + $config->set('var', 'new_value'); + +### Alternative methods for getting / setting config + +In addition to the methods described above you can also access config values using dots to outline a path +from the config group to the value you want: + + // Config file: database.php + return array( + 'default' => array( + 'connection' => array( + 'hostname' => 'localhost' + ) + ) + ); + + // Code which needs hostname: + $hostname = Kohana::$config->load('database.default.connection.hostname'); + + +Which is equivalent to: + + $config = Kohana::$config->load('database')->get('default'); + + $hostname = $config['connection']['hostname']; + +Obviously this method is a lot more compact than the original. However, please bear in mind that using +`dot.notation` is a _lot_ slower than calling `get()` and traversing the array yourself. Dot notation +can be useful if you only need one specific variable, but otherwise it's best to use `get()`. + +As [Config_Group] extends [Array_Object](http://php.net/manual/en/class.arrayobject.php) you can also use array +syntax to get/set config vars: + + $config = Kohana::$config->load('database'); + + // Getting the var + $hostname = $config['default']['connection']['hostname']; + + // Setting the var + $config['default']['connection']['hostname'] = '127.0.0.1'; + +Again, this syntax is more costly than calling `get()` / `set()`. + +## Config Merging + +One of the useful features of the config system is config group merging. This works in a similar way +to the cascading filesystem, with configuration from lower sources lower down the source stack being +merged with sources further up the stack. + +If two sources contain the same config variables then the one from the source further up the stack will +override the one from the "lower" source. However, if the source from higher up the stack does not contain +a particular config variable but a source lower down the stack does then the value from the lower source will +be used. + +The position of sources in the stack is determined by how they are loaded in your bootstrap. +By default when you load a source it is pushed to the top of a stack: + + // Stack: + Kohana::$config->attach(new Config_File); + // Stack: Config_File + Kohana::$config->attach(new Config_Database); + // Stack: Config_Database, Config_File + +In the example above, any config values found in the database will override those found in the filesystem. +For example, using the setup outlined above: + + // Configuration in the filesystem: + email: + sender: + email: my.awesome.address@example.com + name: Unknown + method: smtp + + // Configuration in the database: + email: + sender: + email: my.supercool.address@gmail.com + name: Kohana Bot + + // Configuration returned by Kohana::$config->load('email') + email: + sender: + email: my.supercool.address@gmail.com + name: Kohana Bot + method: smtp + +[!!] **Note:** The above syntax is simply pseudo code to illustrate the concept of config merging. + +On some occasions you may want to append a config source to the bottom of the stack, to do this pass `FALSE` +as the second parameter to `attach()`: + + // Stack: + Kohana::$config->attach(new Config_File); + // Stack: Config_File + Kohana::$config->attach(new Config_Database, FALSE); + // Stack: Config_File, Config_Database + +In this example, any values found in the filesystem will override those found in the db. For example: + + // Configuration in the filesystem: + email: + sender: + email: my.awesome.address@example.com + name: Unknown + method: smtp + + // Configuration in the database: + email: + sender: + email: my.supercool.address@gmail.com + name: Kohana Bot + + // Configuration returned by Kohana::$config->load('email') + email: + sender: + email: my.awesome.address@example.com + name: Unknown + method: smtp + +## Using different config sources based on the environment + +In some situations you'll need to use different config values depending on which state `Kohana::$environment` +is in. Unit testing is a prime example of such a situation. Most setups have two databases; one for normal +development and a separate one for unit testing (to isolate the tests from your development). + +In this case you still need access to the config settings stored in the `config` directory as it contains generic +settings that are needed whatever environment your application is in (e.g. encryption settings), +so replacing the default `Config_File` source isn't really an option. + +To get around this you can attach a separate config file reader which loads its config from a subdir of `config` called +"testing": + + Kohana::$config->attach(new Config_File); + + Kohana::$config->attach(new Config_Database); + + if (Kohana::$environment === Kohana::TESTING) + { + Kohana::$config->attach(new Config_File('config/testing')); + } + +During normal development the config source stack looks like `Config_Database, Config_File('config')`. However, +when `Kohana::$environment === Kohana::TESTING` the stack looks like `Config_File('config/testing'), Config_Database, Config_File('config')` \ No newline at end of file diff --git a/system/guide/kohana/controllers.md b/system/guide/kohana/controllers.md new file mode 100644 index 0000000..d95d98a --- /dev/null +++ b/system/guide/kohana/controllers.md @@ -0,0 +1 @@ +This will discuss controller basics, like before() and after(), private function, and about extending controllers like the Controller_Template, or using a parent::before() for authentication. \ No newline at end of file diff --git a/system/guide/kohana/conventions.md b/system/guide/kohana/conventions.md new file mode 100644 index 0000000..2157178 --- /dev/null +++ b/system/guide/kohana/conventions.md @@ -0,0 +1,418 @@ +# Conventions and Coding Style + +It is encouraged that you follow Kohana's coding style. This makes code more readable and allows for easier code sharing and contributing. + +## Class Names and File Location + +Class names in Kohana follow a strict convention to facilitate [autoloading](autoloading). Class names should have uppercase first letters with underscores to separate words. Underscores are significant as they directly reflect the file location in the filesystem. + +The following conventions apply: + +1. CamelCased class names should be used when it is undesirable to create a new directory level. +2. All class file names and directory names must match the case of the class as per [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md). +3. All classes should be in the `classes` directory. This may be at any level in the [cascading filesystem](files). + +### Examples {#class-name-examples} + +Remember that in a class, an underscore means a new directory. Consider the following examples: + +Class Name | File Path +----------------------|------------------------------- +Controller_Template | classes/Controller/Template.php +Model_User | classes/Model/User.php +Model_BlogPost | classes/Model/BlogPost.php +Database | classes/Database.php +Database_Query | classes/Database/Query.php +Form | classes/Form.php + +## Coding Standards + +In order to produce highly consistent source code, we ask that everyone follow the coding standards as closely as possible. + +### Brackets + +Please use [BSD/Allman Style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracketing. + +#### Curly Brackets + +Curly brackets are placed on their own line, indented to the same level as the control statement. + + // Correct + if ($a === $b) + { + ... + } + else + { + ... + } + + // Incorrect + if ($a === $b) { + ... + } else { + ... + } + +#### Class Brackets + +The only exception to the curly bracket rule is, the opening bracket of a class goes on the same line. + + // Correct + class Foo { + + // Incorrect + class Foo + { + +#### Empty Brackets + +Don't put any characters inside empty brackets. + + // Correct + class Foo {} + + // Incorrect + class Foo { } + +#### Array Brackets + +Arrays may be single line or multi-line. + + array('a' => 'b', 'c' => 'd') + + array( + 'a' => 'b', + 'c' => 'd', + ) + +##### Opening Parenthesis + +The opening array parenthesis goes on the same line. + + // Correct + array( + ... + ) + + // Incorrect: + array + ( + ... + ) + +##### Closing parenthesis + +###### Single Dimension + +The closing parenthesis of a multi-line single dimension array is placed on its own line, indented to the same level as the assignment or statement. + + // Correct + $array = array( + ... + ) + + // Incorrect + $array = array( + ... + ) + +###### Multidimensional + +The nested array is indented one tab to the right, following the single dimension rules. + + // Correct + array( + 'arr' => array( + ... + ), + 'arr' => array( + ... + ), + ) + + array( + 'arr' => array(...), + 'arr' => array(...), + ) + +##### Arrays as Function Arguments + + + // Correct + do(array( + ... + )) + + // Incorrect + do(array( + ... + )) + +As noted at the start of the array bracket section, single line syntax is also valid. + + // Correct + do(array(...)) + + // Alternative for wrapping long lines + do($bar, 'this is a very long line', + array(...)); + +### Naming Conventions + +Kohana uses under_score naming, not camelCase naming. + +#### Classes + + // Controller class, uses Controller_ prefix + class Controller_Apple extends Controller { + + // Model class, uses Model_ prefix + class Model_Cheese extends Model { + + // Regular class + class Peanut { + +When creating an instance of a class, don't use parentheses if you're not passing something on to the constructor: + + // Correct: + $db = new Database; + + // Incorrect: + $db = new Database(); + +#### Functions and Methods + +Functions should be all lowercase, and use under_scores to separate words: + + function drink_beverage($beverage) + { + +#### Variables + +All variables should be lowercase and use under_score, not camelCase: + + // Correct: + $foo = 'bar'; + $long_example = 'uses underscores'; + + // Incorrect: + $weDontWantThis = 'understood?'; + +### Indentation + +You must use tabs to indent your code. Using spaces for tabbing is strictly forbidden. + +Vertical spacing (for multi-line) is done with spaces. Tabs are not good for vertical alignment because different people have different tab widths. + + $text = 'this is a long text block that is wrapped. Normally, we aim for ' + .'wrapping at 80 chars. Vertical alignment is very important for ' + .'code readability. Remember that all indentation is done with tabs,' + .'but vertical alignment should be completed with spaces, after ' + .'indenting with tabs.'; + +### String Concatenation + +Do not put spaces around the concatenation operator: + + // Correct: + $str = 'one'.$var.'two'; + + // Incorrect: + $str = 'one'. $var .'two'; + $str = 'one' . $var . 'two'; + +### Single Line Statements + +Single-line IF statements should only be used when breaking normal execution (e.g. return or continue): + + // Acceptable: + if ($foo == $bar) + return $foo; + + if ($foo == $bar) + continue; + + if ($foo == $bar) + break; + + if ($foo == $bar) + throw new Exception('You screwed up!'); + + // Not acceptable: + if ($baz == $bun) + $baz = $bar + 2; + +### Comparison Operations + +Please use OR and AND for comparison: + + // Correct: + if (($foo AND $bar) OR ($b AND $c)) + + // Incorrect: + if (($foo && $bar) || ($b && $c)) + +Please use elseif, not else if: + + // Correct: + elseif ($bar) + + // Incorrect: + else if($bar) + +### Switch Structures + +Each case, break and default should be on a separate line. The block inside a case or default must be indented by 1 tab. + + switch ($var) + { + case 'bar': + case 'foo': + echo 'hello'; + break; + case 1: + echo 'one'; + break; + default: + echo 'bye'; + break; + } + +### Parentheses + +There should be one space after statement name, followed by a parenthesis. The ! (bang) character must have a space on either side to ensure maximum readability. Except in the case of a bang or type casting, there should be no whitespace after an opening parenthesis or before a closing parenthesis. + + // Correct: + if ($foo == $bar) + if ( ! $foo) + + // Incorrect: + if($foo == $bar) + if(!$foo) + if ((int) $foo) + if ( $foo == $bar ) + if (! $foo) + +### Ternaries + +All ternary operations should follow a standard format. Use parentheses around expressions only, not around just variables. + + $foo = ($bar == $foo) ? $foo : $bar; + $foo = $bar ? $foo : $bar; + +All comparisons and operations must be done inside of a parentheses group: + + $foo = ($bar > 5) ? ($bar + $foo) : strlen($bar); + +When separating complex ternaries (ternaries where the first part goes beyond ~80 chars) into multiple lines, spaces should be used to line up operators, which should be at the front of the successive lines: + + $foo = ($bar == $foo) + ? $foo + : $bar; + +### Type Casting + +Type casting should be done with spaces on each side of the cast: + + // Correct: + $foo = (string) $bar; + if ( (string) $bar) + + // Incorrect: + $foo = (string)$bar; + +When possible, please use type casting instead of ternary operations: + + // Correct: + $foo = (bool) $bar; + + // Incorrect: + $foo = ($bar == TRUE) ? TRUE : FALSE; + +When casting type to integer or boolean, use the short format: + + // Correct: + $foo = (int) $bar; + $foo = (bool) $bar; + + // Incorrect: + $foo = (integer) $bar; + $foo = (boolean) $bar; + +### Constants + +Always use uppercase for constants: + + // Correct: + define('MY_CONSTANT', 'my_value'); + $a = TRUE; + $b = NULL; + + // Incorrect: + define('MyConstant', 'my_value'); + $a = True; + $b = null; + +Place constant comparisons at the end of tests: + + // Correct: + if ($foo !== FALSE) + + // Incorrect: + if (FALSE !== $foo) + +This is a slightly controversial choice, so I will explain the reasoning. If we were to write the previous example in plain English, the correct example would read: + + if variable $foo is not exactly FALSE + +And the incorrect example would read: + + if FALSE is not exactly variable $foo + +Since we are reading left to right, it simply doesn't make sense to put the constant first. + +### Comments + +#### One-line Comments + +Use //, preferably above the line of code you're commenting on. Leave a space after it and start with a capital. Never use #. + + // Correct + + //Incorrect + // incorrect + # Incorrect + +### Regular Expressions + +When coding regular expressions please use PCRE rather than the POSIX flavor. PCRE is considered more powerful and faster. + + // Correct: + if (preg_match('/abc/i', $str)) + + // Incorrect: + if (eregi('abc', $str)) + +Use single quotes around your regular expressions rather than double quotes. Single-quoted strings are more convenient because of their simplicity. Unlike double-quoted strings they don't support variable interpolation nor integrated backslash sequences like \n or \t, etc. + + // Correct: + preg_match('/abc/', $str); + + // Incorrect: + preg_match("/abc/", $str); + +When performing a regular expression search and replace, please use the $n notation for backreferences. This is preferred over \\n. + + // Correct: + preg_replace('/(\d+) dollar/', '$1 euro', $str); + + // Incorrect: + preg_replace('/(\d+) dollar/', '\\1 euro', $str); + +Finally, please note that the $ character for matching the position at the end of the line allows for a following newline character. Use the D modifier to fix this if needed. [More info](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html). + + $str = "email@example.com\n"; + + preg_match('/^.+@.+$/', $str); // TRUE + preg_match('/^.+@.+$/D', $str); // FALSE \ No newline at end of file diff --git a/system/guide/kohana/cookies.md b/system/guide/kohana/cookies.md new file mode 100644 index 0000000..b53a130 --- /dev/null +++ b/system/guide/kohana/cookies.md @@ -0,0 +1,100 @@ +# Cookies + +Kohana provides classes that make it easy to work with both cookies and sessions. At a high level both sessions and cookies provide the same functionality. They allow the developer to store temporary or persistent information about a specific client for later retrieval, usually to make something persistent between requests. + +[Cookies](http://en.wikipedia.org/wiki/HTTP_cookie) should be used for storing non-private data that is persistent for a long period of time. For example storing a user preference or a language setting. Use the [Cookie] class for getting and setting cookies. + +[!!] Kohana uses "signed" cookies. Every cookie that is stored is combined with a secure hash to prevent modification of the cookie. If a cookie is modified outside of Kohana the hash will be incorrect and the cookie will be deleted. This hash is generated using [Cookie::salt()], which uses the [Cookie::$salt] property. You must define this setting in your bootstrap.php: + + Cookie::$salt = 'foobar'; + +Or define an extended cookie class in your application: + + class Cookie extends Kohana_Cookie + { + public static $salt = 'foobar'; + } + +You should set the salt to a secure value. The example above is only for demonstrative purposes. + +Nothing stops you from using `$_COOKIE` like normal, but you can not mix using the Cookie class and the regular `$_COOKIE` global, because the hash that Kohana uses to sign cookies will not be present, and Kohana will delete the cookie. + +## Storing, Retrieving, and Deleting Data + +[Cookie] and [Session] provide a very similar API for storing data. The main difference between them is that sessions are accessed using an object, and cookies are accessed using a static class. + +### Storing Data + +Storing session or cookie data is done using the [Cookie::set] method: + + // Set cookie data + Cookie::set($key, $value); + + // Store a user id + Cookie::set('user_id', 10); + +### Retrieving Data + +Getting session or cookie data is done using the [Cookie::get] method: + + // Get cookie data + $data = Cookie::get($key, $default_value); + + // Get the user id + $user = Cookie::get('user_id'); + +### Deleting Data + +Deleting session or cookie data is done using the [Cookie::delete] method: + + // Delete cookie data + Cookie::delete($key); + + // Delete the user id + Cookie::delete('user_id'); + +## Cookie Settings + +All of the cookie settings are changed using static properties. You can either change these settings in `bootstrap.php` or by using [transparent extension](extension). Always check these settings before making your application live, as many of them will have a direct affect on the security of your application. + +The most important setting is [Cookie::$salt], which is used for secure signing. This value should be changed and kept secret: + + Cookie::$salt = 'your secret is safe with me'; + +[!!] Changing this value will render all cookies that have been set before invalid. + +By default, cookies are stored until the browser is closed. To use a specific lifetime, change the [Cookie::$expiration] setting: + + // Set cookies to expire after 1 week + Cookie::$expiration = 604800; + + // Alternative to using raw integers, for better clarity + Cookie::$expiration = Date::WEEK; + +The path that the cookie can be accessed from can be restricted using the [Cookie::$path] setting. + + // Allow cookies only when going to /public/* + Cookie::$path = '/public/'; + +The domain that the cookie can be accessed from can also be restricted, using the [Cookie::$domain] setting. + + // Allow cookies only on the domain www.example.com + Cookie::$domain = 'www.example.com'; + +If you want to make the cookie accessible on all subdomains, use a dot at the beginning of the domain. + + // Allow cookies to be accessed on example.com and *.example.com + Cookie::$domain = '.example.com'; + +To only allow the cookie to be accessed over a secure (HTTPS) connection, use the [Cookie::$secure] setting. + + // Allow cookies to be accessed only on a secure connection + Cookie::$secure = TRUE; + + // Allow cookies to be accessed on any connection + Cookie::$secure = FALSE; + +To prevent cookies from being accessed using Javascript, you can change the [Cookie::$httponly] setting. + + // Make cookies inaccessible to Javascript + Cookie::$httponly = TRUE; \ No newline at end of file diff --git a/system/guide/kohana/debugging.md b/system/guide/kohana/debugging.md new file mode 100644 index 0000000..251e557 --- /dev/null +++ b/system/guide/kohana/debugging.md @@ -0,0 +1,20 @@ +# Debugging + +Kohana includes several tools to help you debug your application. + +The most basic of these is [Debug::vars]. This simple method will display any number of variables, similar to [var_export](http://php.net/var_export) or [print_r](http://php.net/print_r), but using HTML for extra formatting. + + // Display a dump of the $foo and $bar variables + echo Debug::vars($foo, $bar); + +Kohana also provides a method to show the source code of a particular file using [Debug::source]. + + // Display this line of source code + echo Debug::source(__FILE__, __LINE__); + +If you want to display information about your application files without exposing the installation directory, you can use [Debug::path]: + + // Displays "APPPATH/cache" rather than the real path + echo Debug::path(APPPATH.'cache'); + +If you are having trouble getting something to work correctly, you could check your Kohana logs and your webserver logs, as well as using a debugging tool like [Xdebug](http://www.xdebug.org/). \ No newline at end of file diff --git a/system/guide/kohana/errors.md b/system/guide/kohana/errors.md new file mode 100644 index 0000000..9f40f8b --- /dev/null +++ b/system/guide/kohana/errors.md @@ -0,0 +1,64 @@ +# Error/Exception Handling + +Kohana provides both an exception handler and an error handler that transforms errors into exceptions using PHP's [ErrorException](http://php.net/errorexception) class. Many details of the error and the internal state of the application is displayed by the handler: + +1. Exception class +2. Error level +3. Error message +4. Source of the error, with the error line highlighted +5. A [debug backtrace](http://php.net/debug_backtrace) of the execution flow +6. Included files, loaded extensions, and global variables + +## Example + +Click any of the links to toggle the display of additional information: + +
    {{userguide/examples/error}}
    + +## Disabling Error/Exception Handling + +If you do not want to use the internal error handling, you can disable it (highly discouraged) when calling [Kohana::init]: + + Kohana::init(array('errors' => FALSE)); + +## Error Reporting + +By default, Kohana displays all errors, including strict mode warnings. This is set using [error_reporting](http://php.net/error_reporting): + + error_reporting(E_ALL | E_STRICT); + +When you application is live and in production, a more conservative setting is recommended, such as ignoring notices: + + error_reporting(E_ALL & ~E_NOTICE); + +If you get a white screen when an error is triggered, your host probably has disabled displaying errors. You can turn it on again by adding this line just after your `error_reporting` call: + + ini_set('display_errors', TRUE); + +Errors should **always** be displayed, even in production, because it allows you to use [exception and error handling](debugging.errors) to serve a nice error page rather than a blank white screen when an error happens. + +## HTTP Exception Handling + +Kohana comes with a robust system for handing http errors. It includes exception classes for each http status code. To trigger a 404 in your application (the most common scenario): + + throw HTTP_Exception::factory(404, 'File not found!'); + +To register error pages for these, using 404 as an example: + + class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 { + + public function get_response() + { + $response = Response::factory(); + + $view = View::factory('errors/404'); + + // We're inside an instance of Exception here, all the normal stuff is available. + $view->message = $this->getMessage(); + + $response->body($view->render()); + + return $response; + } + + } \ No newline at end of file diff --git a/system/guide/kohana/extension.md b/system/guide/kohana/extension.md new file mode 100644 index 0000000..f893014 --- /dev/null +++ b/system/guide/kohana/extension.md @@ -0,0 +1,101 @@ +# Transparent Class Extension + +The [cascading filesystem](files) allows transparent class extension. For instance, the class [Cookie] is defined in `SYSPATH/classes/Cookie.php` as: + + class Cookie extends Kohana_Cookie {} + +The default Kohana classes, and many extensions, use this definition so that almost all classes can be extended. You extend any class transparently, by defining your own class in `APPPATH/classes/Cookie.php` to add your own methods. + +[!!] You should **never** modify any of the files that are distributed with Kohana. Always make modifications to classes using transparent extension to prevent upgrade issues. + +For instance, if you wanted to create method that sets encrypted cookies using the [Encrypt] class, you would create a file at `APPPATH/classes/Cookie.php` that extends Kohana_Cookie, and adds your functions: + + encode((string) $value); + + parent::set($name, $value, $expiration); + } + + /** + * Gets an encrypted cookie. + * + * @uses Cookie::get + * @uses Encrypt::decode + */ + public static function decrypt($name, $default = NULL) + { + if ($value = parent::get($name, NULL)) + { + $value = Encrypt::instance(Cookie::$encryption)->decode($value); + } + + return isset($value) ? $value : $default; + } + + } // End Cookie + +Now calling `Cookie::encrypt('secret', $data)` will create an encrypted cookie which we can decrypt with `$data = Cookie::decrypt('secret')`. + +## How it works + +To understand how this works, let's look at what happens normally. When you use the Cookie class, [Kohana::autoload] looks for `classes/Cookie.php` in the [cascading filesystem](files). It looks in `application`, then each module, then `system`. The file is found in `system` and is included. Of course, `system/classes/Cookie.php` is just an empty class which extends `Kohana_Cookie`. Again, [Kohana::autoload] is called this time looking for `classes/Kohana/Cookie.php` which it finds in `system`. + +When you add your transparently extended cookie class at `application/classes/Cookie.php` this file essentially "replaces" the file at `system/classes/Cookie.php` without actually touching it. This happens because this time when we use the Cookie class [Kohana::autoload] looks for `classes/Cookie.php` and finds the file in `application` and includes that one, instead of the one in system. + +## Example: changing [Cookie] settings + +If you are using the [Cookie](cookies) class, and want to change a setting, you should do so using transparent extension, rather than editing the file in the system folder. If you edit it directly, and in the future you upgrade your Kohana version by replacing the system folder, your changes will be reverted and your cookies will probably be invalid. Instead, create a Cookie.php file either in `application/classes/Cookie.php` or a module (`MODPATH//classes/Cookie.php`). + + class Cookie extends Kohana_Cookie { + + // Set a new salt + public $salt = "some new better random salt phrase"; + + // Don't allow javascript access to cookies + public $httponly = TRUE; + + } + +## Example: TODO: an example + +Just post the code and brief description of what function it adds, you don't have to do the "How it works" like above. + +## Example: TODO: something else + +Just post the code and brief description of what function it adds, you don't have to do the "How it works" like above. + +## More examples + +TODO: Provide some links to modules on github, etc that have examples of transparent extension in use. + +## Multiple Levels of Extension + +If you are extending a Kohana class in a module, you should maintain transparent extensions. In other words, do not include any variables or function in the "base" class (eg. Cookie). Instead make your own namespaced class, and have the "base" class extend that one. With our Encrypted cookie example we can create `MODPATH/mymod/Encrypted/Cookie.php`: + + class Encrypted_Cookie extends Kohana_Cookie { + + // Use the same encrypt() and decrypt() methods as above + + } + +And create `MODPATH/mymod/Cookie.php`: + + class Cookie extends Encrypted_Cookie {} + +This will still allow users to add their own extension to [Cookie] while leaving your extensions intact. To do that they would make a cookie class that extends `Encrypted_Cookie` (rather than `Kohana_Cookie`) in their application folder. diff --git a/system/guide/kohana/files.md b/system/guide/kohana/files.md new file mode 100644 index 0000000..5be015e --- /dev/null +++ b/system/guide/kohana/files.md @@ -0,0 +1,83 @@ +# Cascading Filesystem + +The Kohana filesystem is a hierarchy of similar directory structures that cascade. The hierarchy in Kohana (used when a file is loaded by [Kohana::find_file]) is in the following order: + +1. **Application Path** + Defined as `APPPATH` in `index.php`. The default value is `application`. + +2. **Module Paths** + This is set as an associative array using [Kohana::modules] in `APPPATH/bootstrap.php`. Each of the values of the array will be searched **in the order that the modules are defined**. + +3. **System Path** + Defined as `SYSPATH` in `index.php`. The default value is `system`. All of the main or "core" files and classes are defined here. + +Files that are in directories higher up the include path order take precedence over files of the same name lower down the order, which makes it is possible to overload any file by placing a file with the same name in a "higher" directory: + +![Cascading Filesystem Infographic](cascading_filesystem.png) + +This image is only shows certain files, but we can use it to illustrate some examples of the cascading filesystem: + +* If Kohana catches an error, it would display the `kohana/error.php` view, So it would call `Kohana::find_file('views', 'kohana/error')`. This would return `application/views/kohana/error.php` because it takes precidence over `system/views/kohana/error.php`. By doing this we can change the error view without editing the system folder. + +* If we used `View::factory('welcome')` it would call `Kohana::find_file('views','welcome')` which would return `application/views/welcome.php` because it takes precidence over `modules/common/views/welcome.php`. By doing this, you can overwrite things in a module without editing the modules files. + +* If use the Cookie class, [Kohana::auto_load] will call `Kohana::find_file('classes', 'Cookie')` which will return `application/classes/Cookie.php`. Assuming Cookie extends Kohana_Cookie, the autoloader would then call `Kohana::find_file('classes','Kohana/Cookie')` which will return `system/classes/Kohana/Cookie.php` because that file does not exist anywhere higher in the cascade. This is an example of [transparent extension](extension). + +* If you used `View::factory('user')` it would call `Kohana::find_file('views','user')` which would return `modules/common/views/user.php`. + +* If we wanted to change something in `config/database.php` we could copy the file to `application/config/database.php` and make the changes there. Keep in mind that [config files are merged](files/config#merge) rather than overwritten by the cascade. + +## Types of Files + +The top level directories of the application, module, and system paths have the following default directories: + +classes/ +: All classes that you want to [autoload](autoloading) should be stored here. This includes [controllers](mvc/controllers), [models](mvc/models), and all other classes. All classes must follow the [class naming conventions](conventions#class-names-and-file-location) including matching the case of the class i.e. Kohana_Cookie should be stored in classes/Kohana/Cookie.php and not classes/kohana/cookie.php. + +config/ +: Configuration files return an associative array of options that can be loaded using [Kohana::$config]. Config files are merged rather than overwritten by the cascade. See [config files](files/config) for more information. + +i18n/ +: Translation files return an associative array of strings. Translation is done using the `__()` method. To translate "Hello, world!" into Spanish, you would call `__('Hello, world!')` with [I18n::$lang] set to "es-es". I18n files are merged rather than overwritten by the cascade. See [I18n files](files/i18n) for more information. + +messages/ +: Message files return an associative array of strings that can be loaded using [Kohana::message]. Messages and i18n files differ in that messages are not translated, but always written in the default language and referred to by a single key. Message files are merged rather than overwritten by the cascade. See [message files](files/messages) for more information. + +views/ +: Views are plain PHP files which are used to generate HTML or other output. The view file is loaded into a [View] object and assigned variables, which it then converts into an HTML fragment. Multiple views can be used within each other. See [views](mvc/views) for more information. + +*other* +: You can include any other folders in your cascading filesystem. Examples include, but are not limited to, `guide`, `vendor`, `media`, whatever you want. For example, to find `media/logo.png` in the cascading filesystem you would call `Kohana::find_file('media','logo','png')`. + +## Finding Files + +The path to any file within the filesystem can be found by calling [Kohana::find_file]: + + // Find the full path to "classes/Cookie.php" + $path = Kohana::find_file('classes', 'Cookie'); + + // Find the full path to "views/user/login.php" + $path = Kohana::find_file('views', 'user/login'); + +If the file doesn't have a `.php` extension, pass the extension as the third param. + + // Find the full path to "guide/menu.md" + $path = Kohana::find_file('guide', 'menu', 'md'); + + // If $name is "2000-01-01-first-post" this would look for "posts/2000-01-01-first-post.textile" + $path = Kohana::find_file('posts', $name, '.textile'); + + +## Vendor Extensions + +We call extensions or external libraries that are not specific to Kohana "vendor" extensions, and they go in the vendor folder, either in application or in a module. Because these libraries do not follow Kohana's file naming conventions, they cannot be autoloaded by Kohana, so you will have to manually included them. Some examples of vendor libraries are [Markdown](http://daringfireball.net/projects/markdown/), [DOMPDF](http://code.google.com/p/dompdf), [Mustache](http://github.com/bobthecow/mustache.php) and [Swiftmailer](http://swiftmailer.org/). + +For example, if you wanted to use [DOMPDF](http://code.google.com/p/dompdf), you would copy it to `application/vendor/dompdf` and include the DOMPDF autoloading class. It can be useful to do this in a controller's before method, as part of a module's init.php, or the contstructor of a singleton class. + + require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config','inc'); + +Now you can use DOMPDF without loading any more files: + + $pdf = new DOMPDF; + +[!!] If you want to convert views into PDFs using DOMPDF, try the [PDFView](http://github.com/shadowhand/pdfview) module. \ No newline at end of file diff --git a/system/guide/kohana/files/classes.md b/system/guide/kohana/files/classes.md new file mode 100644 index 0000000..0dabf28 --- /dev/null +++ b/system/guide/kohana/files/classes.md @@ -0,0 +1,41 @@ +# Classes + +TODO: Brief intro to classes. + +[Models](mvc/models) and [Controllers](mvc/controllers) are classes as well, but are treated slightly differently by Kohana. Read their respective pages to learn more. + +## Helper or Library? + +Kohana 3 does not differentiate between "helper" classes and "library" classes like in previous versions. They are all placed in the `classes/` folder and follow the same conventions. The distinction is that in general, a "helper" class is used statically, (for examples see the [helpers included in Kohana](helpers)), and library classes are typically instantiated and used as objects (like the [Database query builders](../database/query/builder)). The distinction is not black and white, and is irrelevant anyways, since they are treated the same by Kohana. + +## Creating a class + +To create a new class, simply place a file in the `classes/` directory at any point in the [Cascading Filesystem](files), that follows the [Class naming conventions](conventions#class-names-and-file-location). For example, lets create a `Foobar` class. + + // classes/Foobar.php + + class Foobar { + static function magic() { + // Does something + } + } + +We can now call `Foobar::magic()` any where and Kohana will [autoload](autoloading) the file for us. + +We can also put classes in subdirectories. + + // classes/Professor/Baxter.php + + class Professor_Baxter { + static function teach() { + // Does something + } + } + +We could now call `Professor_Baxter::teach()` any where we want. + +For examples of how to create and use classes, simply look at the 'classes' folder in `system` or any module. + +## Namespacing your classes + +TODO: Discuss namespacing to provide transparent extension functionality in your own classes/modules. diff --git a/system/guide/kohana/files/config.md b/system/guide/kohana/files/config.md new file mode 100644 index 0000000..92484e6 --- /dev/null +++ b/system/guide/kohana/files/config.md @@ -0,0 +1,84 @@ +# Config Files + +Configuration files are used to store any kind of configuration needed for a module, class, or anything else you want. They are plain PHP files, stored in the `config/` directory, which return an associative array: + + 'value', + 'options' => array( + 'foo' => 'bar', + ), + ); + +If the above configuration file was called `myconf.php`, you could access it using: + + $config = Kohana::$config->load('myconf'); + $options = $config->get('options') + +## Merge + +Configuration files are slightly different from most other files within the [cascading filesystem](files) in that they are **merged** rather than overloaded. This means that all configuration files with the same file path are combined to produce the final configuration. The end result is that you can overload *individual* settings rather than duplicating an entire file. + +For example, if we wanted to change or add to an entry in the inflector configuration file, we would not need to duplicate all the other entries from the default configuration file. + + // config/inflector.php + + array( + 'die' => 'dice', // does not exist in default config file + 'mouse' => 'mouses', // overrides 'mouse' => 'mice' in the default config file + ); + + +## Creating your own config files + +Let's say we want a config file to store and easily change things like the title of a website, or the google analytics code. We would create a config file, let's call it `site.php`: + + // config/site.php + + 'Our Shiny Website', + 'analytics' => FALSE, // analytics code goes here, set to FALSE to disable + ); + +We could now call `Kohana::$config->load('site.title')` to get the site name, and `Kohana::$config->load('site.analytics')` to get the analytics code. + +Let's say we want an archive of versions of some software. We could use config files to store each version, and include links to download, documentation, and issue tracking. + + // config/versions.php + + array( + 'codename' => 'Frog', + 'download' => 'files/ourapp-1.0.0.tar.gz', + 'documentation' => 'docs/1.0.0', + 'released' => '06/05/2009', + 'issues' => 'link/to/bug/tracker', + ), + '1.1.0' => array( + 'codename' => 'Lizard', + 'download' => 'files/ourapp-1.1.0.tar.gz', + 'documentation' => 'docs/1.1.0', + 'released' => '10/15/2009', + 'issues' => 'link/to/bug/tracker', + ), + /// ... etc ... + ); + +You could then do the following: + + // In your controller + $view->versions = Kohana::$config->load('versions'); + + // In your view: + foreach ($versions as $version) + { + // echo some html to display each version + } diff --git a/system/guide/kohana/files/i18n.md b/system/guide/kohana/files/i18n.md new file mode 100644 index 0000000..52efc81 --- /dev/null +++ b/system/guide/kohana/files/i18n.md @@ -0,0 +1,67 @@ +# I18n + +Kohana has a fairly simple and easy to use i18n system. It is slightly modeled after gettext, but is not as featureful. If you need the features of gettext, please use that :) + +## __() + +Kohana has a __() function to do your translations for you. This function is only meant for small sections of text, not entire paragraphs or pages of translated text. + +To echo a translated string: + + + +This will echo 'Home' unless you've changed the defined language, which is explained below. + +## Changing the displayed language + +Use the I18n::lang() method to change the displayed language: + + I18n::lang('fr'); + +This will change the language to 'es-es'. + +## Defining language files + +To define the language file for the above language change, create a `i18n/fr.php` that contains: + + 'Bonjour, monde!', + ); + +Now when you do `__('Hello, world!')`, you will get `Bonjour, monde!` + +## I18n variables + +You can define variables in your __() calls like so: + + echo __('Hello, :user', array(':user' => $username)); + +Your i18n key in your translation file will need to be defined as: + + 'Bonjour, :user', + ); + +## Defining your own __() function + +You can define your own __() function by simply defining your own i18n class: + + 'Hello, world!', + ); + +You can also look in subfolders and sub-keys: + + Kohana::message('forms/contact', 'foobar.bar'); + +This will look in the `messages/forms/contact.php` for the `[foobar][bar]` key: + + array( + 'bar' => 'Hello, world!', + ), + ); + +## Notes + + * Don't use __() in your messages files, as these files can be cached and will not work properly. + * Messages are merged by the cascading file system, not overwritten like classes and views. diff --git a/system/guide/kohana/flow.md b/system/guide/kohana/flow.md new file mode 100644 index 0000000..81a2e54 --- /dev/null +++ b/system/guide/kohana/flow.md @@ -0,0 +1,27 @@ +# Request Flow + +Every application follows the same flow: + +1. Application starts from `index.php`. + 1. The application, module, and system paths are set. (`APPPATH`, `MODPATH`, and `SYSPATH`) + 2. Error reporting levels are set. + 3. Install file is loaded, if it exists. + 4. The bootstrap file, `APPPATH/bootstrap.php`, is included. +2. Once we are in `bootstrap.php`: + 6. The [Kohana] class is loaded. + 7. [Kohana::init] is called, which sets up error handling, caching, and logging. + 8. [Kohana_Config] readers and [Kohana_Log] writers are attached. + 9. [Kohana::modules] is called to enable additional modules. + * Module paths are added to the [cascading filesystem](files). + * Includes each module's `init.php` file, if it exists. + * The `init.php` file can perform additional environment setup, including adding routes. + 10. [Route::set] is called multiple times to define the [application routes](routing). + 11. [Request::instance] is called to start processing the request. + 1. Checks each route that has been set until a match is found. + 2. Creates the controller instance and passes the request to it. + 3. Calls the [Controller::before] method. + 4. Calls the controller action, which generates the request response. + 5. Calls the [Controller::after] method. + * The above 5 steps can be repeated multiple times when using [HMVC sub-requests](requests). +3. Application flow returns to index.php + 12. The main [Request] response is displayed \ No newline at end of file diff --git a/system/guide/kohana/fragments.md b/system/guide/kohana/fragments.md new file mode 100644 index 0000000..fa4f0e3 --- /dev/null +++ b/system/guide/kohana/fragments.md @@ -0,0 +1,135 @@ +# Fragments + +Fragments are a quick and simple way to cache HTML or other output. Fragments are not useful for caching objects or raw database results, in which case you should use a more robust caching method, which can be achieved with the [Cache module](../cache). Fragments use [Kohana::cache()] and will be placed in the cache directory (`application/cache` by default). + +You should use Fragment (or any caching solution) when reading the cache is faster than reprocessing the result. Reading and parsing a remote file, parsing a complicated template, calculating something, etc. + +Fragments are typically used in view files. + +## Usage + +Fragments are used by calling [Fragment::load()] in an `if` statement at the beginning of what you want cached, and [Fragment::save()] at the end. They use [output buffering](http://www.php.net/manual/en/function.ob-start.php) to capture the output between the two function calls. + +You can specify the lifetime (in seconds) of the Fragment using the second parameter of [Fragment::load()]. The default lifetime is 30 seconds. You can use the [Date] helper to make more readable times. + +Fragments will store a different cache for each language (using [I18n]) if you pass `true` as the third parameter to [Fragment::load()]; + +You can force the deletion of a Fragment using [Fragment::delete()], or specify a lifetime of 0. + +~~~ +// Cache for 5 minutes, and cache each language +if ( ! Fragment::load('foobar', Date::MINUTE * 5, true)) +{ + // Anything that is echo'ed here will be saved + Fragment::save(); +} +~~~ + +## Example: Calculating Pi + +In this example we will calculate pi to 1000 places, and cache the result using a fragment. The first time you run this it will probably take a few seconds, but subsequent loads will be much faster, until the fragment lifetime runs out. + +~~~ +if ( ! Fragment::load('pi1000', Date::HOUR * 4)) +{ + // Change function nesting limit + ini_set('xdebug.max_nesting_level',1000); + + // Source: http://mgccl.com/2007/01/22/php-calculate-pi-revisited + function bcfact($n) + { + return ($n == 0 || $n== 1) ? 1 : bcmul($n,bcfact($n-1)); + } + function bcpi($precision) + { + $num = 0;$k = 0; + bcscale($precision+3); + $limit = ($precision+3)/14; + while($k < $limit) + { + $num = bcadd($num, bcdiv(bcmul(bcadd('13591409',bcmul('545140134', $k)),bcmul(bcpow(-1, $k), bcfact(6*$k))),bcmul(bcmul(bcpow('640320',3*$k+1),bcsqrt('640320')), bcmul(bcfact(3*$k), bcpow(bcfact($k),3))))); + ++$k; + } + return bcdiv(1,(bcmul(12,($num))),$precision); + } + + echo bcpi(1000); + + Fragment::save(); +} + +echo View::factory('profiler/stats'); + +?> +~~~ + +## Example: Recent Wikipedia edits + +In this example we will use the [Feed] class to retrieve and parse an RSS feed of recent edits to [http://en.wikipedia.org](http://en.wikipedia.org), then use Fragment to cache the results. + +~~~ +$feed = "http://en.wikipedia.org/w/index.php?title=Special:RecentChanges&feed=rss"; +$limit = 50; + +// Displayed feeds are cached for 30 seconds (default) +if ( ! Fragment::load('rss:'.$feed)): + + // Parse the feed + $items = Feed::parse($feed, $limit); + + foreach ($items as $item): + + // Convert $item to object + $item = (object) $item; + + echo HTML::anchor($item->link,$item->title); + + ?> +
    +

    author: creator ?>

    +

    date: pubDate ?>

    +
    + Home page stuff

    "; + + // Pretend like we are actually doing something :) + sleep(2); + + // Cache this every hour since it doesn't change as often + if ( ! Fragment::load('homepage-subfragment', Date::HOUR)): + + echo "

    Home page special thingy

    "; + + // Pretend like this takes a long time + sleep(5); + + Fragment::save(); endif; + + echo "

    More home page stuff

    "; + + Fragment::save(); + +endif; + +echo View::factory('profiler/stats'); +~~~ \ No newline at end of file diff --git a/system/guide/kohana/helpers.md b/system/guide/kohana/helpers.md new file mode 100644 index 0000000..fb0559d --- /dev/null +++ b/system/guide/kohana/helpers.md @@ -0,0 +1,53 @@ +# Helpers + +Kohana comes with many static "helper" functions to make certain tasks easier. + +You can make your own helpers by simply making a class and putting it in the `classes` directory, and you can also extend any helper to modify or add new functions using transparent extension. + + - **[Arr]** - Array functions. Get an array key or default to a set value, get an array key by path, etc. + + - **[CLI]** - Parse command line options. + + - **[Cookie]** - Covered in more detail on the [Cookies](cookies) page. + + - **[Date]** - Useful date functions and constants. Time between two dates, convert between am/pm and military, date offset, etc. + + - **[Encrypt]** - Covered in more detail on the [Security](security) page. + + - **[Feed]** - Parse and create RSS feeds. + + - **[File]** - Get file type by mime, split and merge a file into small pieces. + + - **[Form]** - Create HTML form elements. + + - **[Fragment]** - Simple file based caching. Covered in more detail on the [Fragments](fragments) page. + + - **[HTML]** - Useful HTML functions. Encode, obfuscate, create script, anchor, and image tags, etc. + + - **[I18n]** - Internationalization helper for creating multilanguage sites. + + - **[Inflector]** - Change a word into plural or singular form, camelize or humanize a phrase, etc. + + - **[Kohana]** - The Kohana class is also a helper. Debug variables (like print_r but better), file loading, etc. + + - **[Num]** - Provides locale aware formating and english ordinals (th, st, nd, etc). + + - **[Profiler]** - Covered in more detail on the [Profiling](profiling) page. + + - **[Remote]** - Remote server access helper using [CURL](http://php.net/curl). + + - **[Request]** - Get the current request url, create expire tags, send a file, get the user agent, etc. + + - **[Route]** - Create routes, create an internal link using a route. + + - **[Security]** - Covered in more detail on the [Security](security) page. + + - **[Session]** - Covered in more detail on the [Sessions](sessions) page. + + - **[Text]** - Autolink, prevent window words, convert a number to text, etc. + + - **[URL]** - Create a relative or absolute URL, make a URL-safe title, etc. + + - **[UTF8]** - Provides multi-byte aware string functions like strlen, strpos, substr, etc. + + - **[Upload]** - Helper for uploading files from a form. diff --git a/system/guide/kohana/index.md b/system/guide/kohana/index.md new file mode 100644 index 0000000..699a5c1 --- /dev/null +++ b/system/guide/kohana/index.md @@ -0,0 +1,19 @@ +# What is Kohana? + +Kohana is an open source, [object oriented](http://en.wikipedia.org/wiki/Object-oriented_programming) [MVC](http://en.wikipedia.org/wiki/Model–view–controller "Model View Controller") [web framework](http://en.wikipedia.org/wiki/Web_application_framework) built using [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") by a team of volunteers that aims to be swift, secure, and small. + +[!!] Kohana is licensed under a [BSD license](http://kohanaframework.org/license), so you can legally use it for any kind of open source, commercial, or personal project. + +## What makes Kohana great? + +Anything can be extended using the unique [filesystem](files) design, little or no [configuration](config) is necessary, [error handling](errors) helps locate the source of errors quickly, and [debugging](debugging) and [profiling](profiling) provide insight into the application. + +To help secure your applications, tools for [input validation](security/validation), [signed cookies](security/cookies), [form] and [HTML] generators are all included. The [database](security/database) layer provides protection against [SQL injection](http://wikipedia.org/wiki/SQL_injection). Of course, all official code is carefully written and reviewed for security. + +## Contribute to the Documentation + +We are working very hard to provide complete documentation. To help improve the guide, please [fork the userguide](http://github.com/kohana/userguide), make your changes, and send a pull request. If you are not familiar with Git, you can also submit a [feature request](http://dev.kohanaframework.org/projects/kohana3/issues) (requires registration). + +## Unofficial Documentation + +If you are having trouble finding an answer here, have a look through the [unofficial wiki](http://kerkness.ca/kowiki/doku.php). Your answer may also be found by searching the [forum](http://forum.kohanaframework.org/) or [Stack Overflow](http://stackoverflow.com/questions/tagged/kohana) followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode [#kohana](irc://irc.freenode.net/kohana) IRC channel. \ No newline at end of file diff --git a/system/guide/kohana/install.md b/system/guide/kohana/install.md new file mode 100644 index 0000000..77b504d --- /dev/null +++ b/system/guide/kohana/install.md @@ -0,0 +1,45 @@ +# Installation + +1. Download the latest **stable** release from the [Kohana website](http://kohanaframework.org/). +2. Unzip the downloaded package to create a `kohana` directory. +3. Upload the contents of this folder to your webserver. +4. Open `application/bootstrap.php` and make the following changes: + - Set the default [timezone](http://php.net/timezones) for your application. + + ~~~ + // Example of changing timezone from Chicago to Sao Paulo, Brazil + date_default_timezone_set('America/Sao_Paulo'); + ~~~ + - Set the `base_url` in the [Kohana::init] call to reflect the location of the kohana folder on your server relative to the document root. + + ~~~ + // Example of kohana's installation at /var/www/mywebsite and + // Apache's DocumentRoot configured to /var/www + Kohana::init(array( + 'base_url' => '/mywebsite', + )); + ~~~ +6. Make sure the `application/cache` and `application/logs` directories are writable by the web server. + + ~~~ + sudo chmod 777 -R application/cache + sudo chmod 777 -R application/logs + ~~~ + +7. Test your installation by opening the URL you set as the `base_url` in your favorite browser. + +[!!] Depending on your platform, the installation's subdirs may have lost their permissions thanks to zip extraction. Chmod them all to 755 by running `find . -type d -exec chmod 0755 {} \;` from the root of your Kohana installation. + +You should see the installation page. If it reports any errors, you will need to correct them before continuing. + +![Install Page](install.png "Example of install page") + +Once your install page reports that your environment is set up correctly you need to either rename or delete `install.php` in the root directory. Kohana is now installed and you should see the output of the welcome controller: + +![Welcome Page](welcome.png "Example of welcome page") + +## Installing Kohana From GitHub + +The [source code](http://github.com/kohana/kohana) for Kohana is hosted with [GitHub](http://github.com). To install Kohana using the github source code first you need to install [git](http://git-scm.com/). Visit [http://help.github.com](http://help.github.com) for details on how to install git on your platform. + +[!!] For more information on installing Kohana using git submodules, see the [Working with Git](tutorials/git) tutorial. \ No newline at end of file diff --git a/system/guide/kohana/menu.md b/system/guide/kohana/menu.md new file mode 100644 index 0000000..a124623 --- /dev/null +++ b/system/guide/kohana/menu.md @@ -0,0 +1,49 @@ +## [Kohana]() + +- Getting Started + - [Installation](install) + - [Conventions and Style](conventions) + - [Model View Controller](mvc) + - [Controllers](mvc/controllers) + - [Models](mvc/models) + - [Views](mvc/views) + - [Cascading Filesystem](files) + - [Class Files](files/classes) + - [Config Files](files/config) + - [Translation Files](files/i18n) + - [Message Files](files/messages) + - [Configuration](config) + - [Request Flow](flow) + - [Bootstrap](bootstrap) + - [Modules](modules) + - [Routing](routing) + - [Error Handling](errors) + - [Tips & Common Mistakes](tips) + - [Upgrading from v3.2](upgrading) +- Basic Usage + - [Debugging](debugging) + - [Loading Classes](autoloading) + - [Transparent Extension](extension) + - [Helpers](helpers) + - [Requests](requests) + - [Sessions](sessions) + - [Cookies](cookies) + - [Fragments](fragments) + - [Profiling](profiling) +- [Security](security) + - [XSS](security/xss) + - [Validation](security/validation) + - [Cookies](security/cookies) + - [Database](security/database) + - [Encryption](security/encryption) + - [Deploying](security/deploying) +- [Tutorials](tutorials) + - [Hello World](tutorials/hello-world) + - [Simple MVC](tutorials/simple-mvc) + - [Custom Error Pages](tutorials/error-pages) + - [Content Translation](tutorials/translation) + - [Clean URLs](tutorials/clean-urls) + - [Sharing Kohana](tutorials/sharing-kohana) + - [Kohana as a Library](tutorials/library-kohana) + - [Template Driven Site](tutorials/templates) + - [Working with Git](tutorials/git) diff --git a/system/guide/kohana/modules.md b/system/guide/kohana/modules.md new file mode 100644 index 0000000..bc906c4 --- /dev/null +++ b/system/guide/kohana/modules.md @@ -0,0 +1,40 @@ +# Modules + +Modules are simply an addition to the [Cascading Filesystem](files). A module can add any kind of file (controllers, views, classes, config files, etc.) to the filesystem available to Kohana (via [Kohana::find_file]). This is useful to make any part of your application more transportable or shareable between different apps. For example, creating a new modeling system, a search engine, a css/js manager, etc. + +## Where to find modules + +Kolanos has created [kohana-universe](http://github.com/kolanos/kohana-universe/tree/master/modules/), a fairly comprehensive list of modules that are available on Github. To get your module listed there, send him a message via Github. + +Mon Geslani created a [very nice site](http://kohana.mongeslani.com/) that allows you to sort Github modules by activity, watchers, forks, etc. It seems to not be as comprehensive as kohana-universe. + +Andrew Hutchings has created [kohana-modules](http://www.kohana-modules.com) which is similar to the above sites. + +## Enabling modules + +Modules are enabled by calling [Kohana::modules] and passing an array of `'name' => 'path'`. The name isn't important, but the path obviously is. A module's path does not have to be in `MODPATH`, but usually is. You can only call [Kohana::modules] once. + + Kohana::modules(array( + 'auth' => MODPATH.'auth', // Basic authentication + 'cache' => MODPATH.'cache', // Caching with multiple backends + 'codebench' => MODPATH.'codebench', // Benchmarking tool + 'database' => MODPATH.'database', // Database access + 'image' => MODPATH.'image', // Image manipulation + 'orm' => MODPATH.'orm', // Object Relationship Mapping + 'oauth' => MODPATH.'oauth', // OAuth authentication + 'pagination' => MODPATH.'pagination', // Paging of results + 'unittest' => MODPATH.'unittest', // Unit testing + 'userguide' => MODPATH.'userguide', // User guide and API documentation + )); + +## Init.php + +When a module is activated, if an `init.php` file exists in that module's directory, it is included. This is the ideal place to have a module include routes or other initialization necessary for the module to function. The Userguide and Codebench modules have init.php files you can look at. + +## How modules work + +A file in an enabled module is virtually the same as having that exact file in the same place in the application folder. The main difference being that it can be overwritten by a file of the same name in a higher location (a module enabled after it, or the application folder) via the [Cascading Filesystem](files). It also provides an easy way to organize and share your code. + +## Creating your own module + +To create a module simply create a folder (usually in `DOCROOT/modules`) and place the files you want to be in the module there, and activate that module in your bootstrap. To share your module, you can upload it to [Github](http://github.com). You can look at examples of modules made by [Kohana](http://github.com/kohana) or [other users](#where-to-find-modules). \ No newline at end of file diff --git a/system/guide/kohana/mvc.md b/system/guide/kohana/mvc.md new file mode 100644 index 0000000..d57b751 --- /dev/null +++ b/system/guide/kohana/mvc.md @@ -0,0 +1,3 @@ + + +Discuss the MVC pattern, as it pertains to Kohana. Perhaps have an image, etc. \ No newline at end of file diff --git a/system/guide/kohana/mvc/controllers.md b/system/guide/kohana/mvc/controllers.md new file mode 100644 index 0000000..52b4181 --- /dev/null +++ b/system/guide/kohana/mvc/controllers.md @@ -0,0 +1,181 @@ +# Controllers + +A Controller is a class file that stands in between the models and the views in an application. It passes information on to the model when data needs to be changed and it requests information from the model when data needs to be loaded. Controllers then pass on the information of the model to the views where the final output can be rendered for the users. Controllers essentially control the flow of the application. + +Controllers are called by the [Request::execute()] function based on the [Route] that the url matched. Be sure to read the [routing](routing) page to understand how to use routes to map urls to your controllers. + +## Creating Controllers + +In order to function, a controller must do the following: + +* Reside in `classes/Controller` (or a sub-directory) +* Filename must match the class name exactly, e.g. `Articles.php` +* The class name must map to the filename (with `/` replaced with `_`) and each word is capitalized +* Must have the Controller class as a (grand)parent + +Some examples of controller names and file locations: + + // classes/Controller/Foobar.php + class Controller_Foobar extends Controller { + + // classes/Controller/Admin.php + class Controller_Admin extends Controller { + +Controllers can be in sub-folders: + + // classes/Controller/Baz/Bar.php + class Controller_Baz_Bar extends Controller { + + // classes/Controller/Product/Category.php + class Controller_Product_Category extends Controller { + +[!!] Note that controllers in sub-folders can not be called by the default route, you will need to define a route that has a [directory](routing#directory) param or sets a default value for directory. + +Controllers can extend other controllers. + + // classes/Controller/Users.php + class Controller_Users extends Controller_Template + + // classes/Controller/Api.php + class Controller_Api extends Controller_REST + +[!!] [Controller_Template] is an example controller provided in Kohana. + +You can also have a controller extend another controller to share common things, such as requiring you to be logged in to use all of those controllers. + + // classes/Controller/Admin.php + class Controller_Admin extends Controller { + // This controller would have a before() that checks if the user is logged in + + // classes/Controller/Admin/Plugins.php + class Controller_Admin_Plugins extends Controller_Admin { + // Because this controller extends Controller_Admin, it would have the same logged in check + +## $this->request + +Every controller has the `$this->request` property which is the [Request] object that called the controller. You can use this to get information about the current request, as well as set the response body via `$this->response->body($ouput)`. + +Here is a partial list of the properties and methods available to `$this->request`. These can also be accessed via `Request::instance()`, but `$this->request` is provided as a shortcut. See the [Request] class for more information on any of these. + +Property/method | What it does +--- | --- +[$this->request->route()](../api/Request#property:route) | The [Route] that matched the current request url +[$this->request->directory()](../api/Request#property:directory),
    [$this->request->controller](../api/Request#property:controller),
    [$this->request->action](../api/Request#property:action) | The directory, controller and action that matched for the current route +[$this->request->param()](../api/Request#param) | Any other params defined in your route + +## $this->response +[$this->response->body()](../api/Response#property:body) | The content to return for this request +[$this->response->status()](../api/Response#property:status) | The HTTP status for the request (200, 404, 500, etc.) +[$this->response->headers()](../api/Response#property:headers) | The HTTP headers to return with the response + + +## Actions + +You create actions for your controller by defining a public function with an `action_` prefix. Any method that is not declared as `public` and prefixed with `action_` can NOT be called via routing. + +An action method will decide what should be done based on the current request, it *controls* the application. Did the user want to save a blog post? Did they provide the necessary fields? Do they have permission to do that? The controller will call other classes, including models, to accomplish this. Every action should set `$this->response->body($view)` to the [view file](mvc/views) to be sent to the browser, unless it redirected or otherwise ended the script earlier. + +A very basic action method that simply loads a [view](mvc/views) file. + + public function action_hello() + { + $this->response->body(View::factory('hello/world')); // This will load views/hello/world.php + } + +### Parameters + +Parameters are accessed by calling `$this->request->param('name')` where `name` is the name defined in the route. + + // Assuming Route::set('example','(/(/(/)))'); + + public function action_foobar() + { + $id = $this->request->param('id'); + $new = $this->request->param('new'); + +If that parameter is not set it will be returned as NULL. You can provide a second parameter to set a default value if that param is not set. + + public function action_foobar() + { + // $id will be false if it was not supplied in the url + $id = $this->request->param('user',FALSE); + +### Examples + +A view action for a product page. + + public function action_view() + { + $product = new Model_Product($this->request->param('id')); + + if ( ! $product->loaded()) + { + throw HTTP_Exception::factory(404, 'Product not found!'); + } + + $this->response->body(View::factory('product/view') + ->set('product', $product)); + } + +A user login action. + + public function action_login() + { + $view = View::factory('user/login'); + + if ($this->request->post()) + { + // Try to login + if (Auth::instance()->login($this->request->post('username'), $this->request->post('password'))) + { + $this->redirect('home', 302); + } + + $view->errors = 'Invalid email or password'; + } + + $this->response->body($view); + } + +## Before and after + +You can use the `before()` and `after()` functions to have code executed before or after the action is executed. For example, you could check if the user is logged in, set a template view, loading a required file, etc. + +For example, if you look in `Controller_Template` you can see that in the be + +You can check what action has been requested (via `$this->request->action`) and do something based on that, such as requiring the user to be logged in to use a controller, unless they are using the login action. + + // Checking auth/login in before, and redirecting if necessary: + + Controller_Admin extends Controller { + + public function before() + { + // If this user doesn't have the admin role, and is not trying to login, redirect to login + if ( ! Auth::instance()->logged_in('admin') AND $this->request->action !== 'login') + { + $this->redirect('admin/login', 302); + } + } + + public function action_login() { + ... + +### Custom __construct() function + +In general, you should not have to change the `__construct()` function, as anything you need for all actions can be done in `before()`. If you need to change the controller constructor, you must preserve the parameters or PHP will complain. This is so the Request object that called the controller is available. *Again, in most cases you should probably be using `before()`, and not changing the constructor*, but if you really, *really* need to it should look like this: + + // You should almost never need to do this, use before() instead! + + // Be sure Kohana_Request is in the params + public function __construct(Request $request, Response $response) + { + // You must call parent::__construct at some point in your function + parent::__construct($request, $response); + + // Do whatever else you want + } + +## Extending other controllers + +TODO: More description and examples of extending other controllers, multiple extension, etc. diff --git a/system/guide/kohana/mvc/models.md b/system/guide/kohana/mvc/models.md new file mode 100644 index 0000000..bfcf240 --- /dev/null +++ b/system/guide/kohana/mvc/models.md @@ -0,0 +1,35 @@ +# Models + +From Wikipedia: + + > The model manages the behavior and data of the application domain, + > responds to requests for information about its state (usually from the view), + > and responds to instructions to change state (usually from the controller). + +Creating a simple model: + + class Model_Post extends Model + { + public function do_stuff() + { + // This is where you do domain logic... + } + } + +If you want database access, have your model extend the Model_Database class: + + class Model_Post extends Model_Database + { + public function do_stuff() + { + // This is where you do domain logic... + } + + public function get_stuff() + { + // Get stuff from the database: + return $this->db->query(...); + } + } + +If you want CRUD/ORM capabilities, see the [ORM Module](../../guide/orm) \ No newline at end of file diff --git a/system/guide/kohana/mvc/views.md b/system/guide/kohana/mvc/views.md new file mode 100644 index 0000000..4ff0543 --- /dev/null +++ b/system/guide/kohana/mvc/views.md @@ -0,0 +1,153 @@ +# Views + +Views are files that contain the display information for your application. This is most commonly HTML, CSS and Javascript but can be anything you require such as XML or JSON for AJAX output. The purpose of views is to keep this information separate from your application logic for easy reusability and cleaner code. + +Views themselves can contain code used for displaying the data you pass into them. For example, looping through an array of product information and display each one on a new table row. Views are still PHP files so you can use any code you normally would. However, you should try to keep your views as "dumb" as possible and retreive all data you need in your controllers, then pass it to the view. + +# Creating View Files + +View files are stored in the `views` directory of the [filesystem](files). You can also create sub-directories within the `views` directory to organize your files. All of the following examples are reasonable view files: + + APPPATH/views/home.php + APPPATH/views/pages/about.php + APPPATH/views/products/details.php + MODPATH/error/views/errors/404.php + MODPATH/common/views/template.php + +## Loading Views + +[View] objects will typically be created inside a [Controller](mvc/controllers) using the [View::factory] method. Typically the view is then assigned as the [Request::$response] property or to another view. + + public function action_about() + { + $this->response->body(View::factory('pages/about')); + } + +When a view is assigned as the [Response::body], as in the example above, it will automatically be rendered when necessary. To get the rendered result of a view you can call the [View::render] method or just type cast it to a string. When a view is rendered, the view file is loaded and HTML is generated. + + public function action_index() + { + $view = View::factory('pages/about'); + + // Render the view + $about_page = $view->render(); + + // Or just type cast it to a string + $about_page = (string) $view; + + $this->response->body($about_page); + } + +## Variables in Views + +Once view has been loaded, variables can be assigned to it using the [View::set] and [View::bind] methods. + + public function action_roadtrip() + { + $view = View::factory('user/roadtrip') + ->set('places', array('Rome', 'Paris', 'London', 'New York', 'Tokyo')); + ->bind('user', $this->user); + + // The view will have $places and $user variables + $this->response->body($view); + } + +[!!] The only difference between `set()` and `bind()` is that `bind()` assigns the variable by reference. If you `bind()` a variable before it has been defined, the variable will be created with a value of `NULL`. + +You can also assign variables directly to the View object. This is identical to calling `set()`; + + public function action_roadtrip() + { + $view = View::factory('user/roadtrip'); + + $view->places = array('Rome', 'Paris', 'London', 'New York', 'Tokyo'); + $view->user = $this->user; + + // The view will have $places and $user variables + $this->response->body($view); + } + +### Global Variables + +An application may have several view files that need access to the same variables. For example, to display a page title in both the header of your template and in the body of the page content. You can create variables that are accessible in any view using the [View::set_global] and [View::bind_global] methods. + + // Assign $page_title to all views + View::bind_global('page_title', $page_title); + +If the application has three views that are rendered for the home page: `template`, `template/sidebar`, and `pages/home`. First, an abstract controller to create the template will be created: + + abstract class Controller_Website extends Controller_Template { + + public $page_title; + + public function before() + { + parent::before(); + + // Make $page_title available to all views + View::bind_global('page_title', $this->page_title); + + // Load $sidebar into the template as a view + $this->template->sidebar = View::factory('template/sidebar'); + } + + } + +Next, the home controller will extend `Controller_Website`: + + class Controller_Home extends Controller_Website { + + public function action_index() + { + $this->page_title = 'Home'; + + $this->template->content = View::factory('pages/home'); + } + + } + +## Views Within Views + +If you want to include another view within a view, there are two choices. By calling [View::factory] you can sandbox the included view. This means that you will have to provide all of the variables to the view using [View::set] or [View::bind]: + + // In your view file: + + // Only the $user variable will be available in "views/user/login.php" + bind('user', $user) ?> + +The other option is to include the view directly, which makes all of the current variables available to the included view: + + // In your view file: + + // Any variable defined in this view will be included in "views/message.php" + + +You can also assign a variable of your parent view to be the child view from within your controller. For example: + + // In your controller: + + public functin action_index() + { + $view = View::factory('common/template); + + $view->title = "Some title"; + $view->body = View::factory('pages/foobar'); + } + + // In views/common/template.php: + + + + <?php echo $title> + + + + + + + +Of course, you can also load an entire [Request] within a view: + + execute() ?> + +This is an example of \[HMVC], which makes it possible to create and read calls to other URLs within your application. \ No newline at end of file diff --git a/system/guide/kohana/profiling.md b/system/guide/kohana/profiling.md new file mode 100644 index 0000000..e7303c8 --- /dev/null +++ b/system/guide/kohana/profiling.md @@ -0,0 +1,54 @@ +# Profiling + +Kohana provides a very simple way to display statistics about your application: + +1. Common [Kohana] method calls, such as [Kohana::find_file()]. +2. Requests. Including the main request, as well as any sub-requests. +3. [Database] queries +4. Average execution times for your application + +[!!] In order for profiling to work, the `profile` setting must be `TRUE` in your [Kohana::init()] call in your bootstrap. + +## Profiling your code + +You can easily add profiling to your own functions and code. This is done using the [Profiler::start()] function. The first parameter is the group, the second parameter is the name of the benchmark. + + public function foobar($input) + { + // Be sure to only profile if it's enabled + if (Kohana::$profiling === TRUE) + { + // Start a new benchmark + $benchmark = Profiler::start('Your Category', __FUNCTION__); + } + + // Do some stuff + + if (isset($benchmark)) + { + // Stop the benchmark + Profiler::stop($benchmark); + } + + return $something; + } + +## How to read the profiling report + +The benchmarks are sorted into groups. Each benchmark will show its name, how many times it was run (show in parenthesis after the benchmark name), and then the min, max, average, and total time and memory spent on that benchmark. The total column will have shaded backgrounds to show the relative times between benchmarks in the same group. + +At the very end is a group called "Application Execution". This keeps track of how long each execution has taken. The number in parenthesis is how many executions are being compared. It shows the fastest, slowest, and average time and memory usage of the last several requsets. The last box is the time and memory usage of the current request. + +((This could use a picture of a profiler with some database queries, etc. with annotations to point out each area as just described.)) + +## Displaying the profiler + +You can display or collect the current [profiler] statistics at any time: + + + +## Preview + +(This is the actual profiler stats for this page.) + +{{profiler/stats}} \ No newline at end of file diff --git a/system/guide/kohana/requests.md b/system/guide/kohana/requests.md new file mode 100644 index 0000000..eca76a6 --- /dev/null +++ b/system/guide/kohana/requests.md @@ -0,0 +1,150 @@ +# Requests + +Kohana includes a flexible HMVC request system. It supports out of the box support for internal requests and external requests. + +HMVC stands for `Hierarchical Model View Controller` and basically means requests can each have MVC triads called from inside each other. + +The Request object in Kohana is HTTP/1.1 compliant. + +## Creating Requests + +Creating a request is very easy: + +### Internal Requests + +An internal request is a request calling to the internal application. It utilizes [routes](routing) to direct the application based on the URI that is passed to it. A basic internal request might look something like: + + $request = Request::factory('welcome'); + +In this example, the URI is 'welcome'. + +#### The initial request + +Since Kohana uses HMVC, you can call many requests inside each other. The first request (usually called from `index.php`) is called the "initial request". You can access this request via: + + Request::initial(); + +You should only use this method if you are absolutely sure you want the initial request. Otherwise you should use the `Request::current()` method. + +#### Sub-requests + +You can call a request at any time in your application by using the `Request::factory()` syntax. All of these requests will be considered sub-requests. + +Other than this difference, they are exactly the same. You can detect if the request is a sub-request in your controller with the is_initial() method: + + $sub_request = ! $this->request->is_initial() + +### External Requests + +An external request calls out to a third party website. + +You can use this to scrape HTML from a remote site, or make a REST call to a third party API: + + // This uses GET + $request = Request::factory('http://www.google.com/'); + + // This uses PUT + $request = Request::factory('http://example.com/put_api')->method(Request::PUT)->body(json_encode('the body'))->headers('Content-Type', 'application/json'); + + // This uses POST + $request = Request::factory('http://example.com/post_api')->method(Request::POST)->post(array('foo' => 'bar', 'bar' => 'baz')); + +## Executing Requests + +To execute a request, use the `execute()` method on it. This will give you a [response](responses) object. + + $request = Request::factory('welcome'); + $response = $request->execute(); + +### Header callbacks +The request client supports header callbacks - an array of callbacks that will be triggered when a specified header is included in the response from a server. Header callbacks provide a powerful way to deal with scenarios including authentication, rate limiting, redirects and other application-specific use cases: + + $request = Request::factory('http://example.com/user', array( + 'header_callbacks' => array( + 'Content-Encoding' => + function (Request $request, Response $response, Request_Client $client) + { + // Uncompress the response + $response->body(GZIP::expand($response->body())); + }, + 'X-Rate-Limited' => + function (Request $request, Response $response, Request_Client $client) + { + // Log the rate limit event + // And perhaps set a deadlock in cache to prevent further requests + }, + 'WWW-Authenticate' => + function (Request $request, Response $response, Request_Client $client) + { + // Execute a request to refresh your OAuth token somehow + // Have the original request resent + return Request::factory($request->uri()) + ->query($request->query()) + ->headers('Authorization', 'token'.$token); + })); + +Where multiple headers are present in the response, callbacks will be executed in sequence. Callbacks can be any valid PHP callback type and have three possible return types: + +Type | Function +------------------|--------- +[Request] object | If a new request is returned, the request client will automatically assign properties, callbacks etc to match the original request and then execute the request. No further callbacks will be triggered for the original request, but the new request may trigger callbacks when executed. +[Response] object | If the callback returns a new response instance it will be returned to the application. No further callbacks will be triggered for the original request. The callback is responsible for setting any relevant callbacks and properties for the request it executes +NULL | The callback can, if required, modify the provided Response object and return NULL. The modified response object will be passed into subsequent callbacks. + +#### Nested requests +If your callback returns a new Request object, the request client will apply the same callback and property definitions to it before execution. This allows for nested requests - for example, you might need to re-authenticate before submitting a POST request and then being redirected to a new location. To avoid infinite recursion and fatal errors, the request client keeps track of the number of subrequests and will throw a [Request_Client_Recursion_Exception] if the recursion gets too deep. This behaviour is controlled by two properties: [Request_Client::callback_depth()] and [Request_Client::max_callback_depth()]. The default limit is 5 subrequests. + +If your callback executes a new request itself and returns the response, it is responsible for dealing with any callbacks and request nesting itself. You may find the [Request_Client::assign_client_properties()] method useful in this case. + +#### Callback parameters +Arbitrary parameters can be passed to the callbacks through the [Request_Client::callback_params()] property: + + $request = Request::factory('http://example.com/foo', array( + 'header_callbacks' => array( + 'X-Custom-1' => + function (Request $request, Response $response, Request_Client $client) + { + // Do something that needs an external parameter + if ($client->callback_params('foo') == 'bar') + { + // etc + } + }, + ) + 'callback_params' => array( + 'foo' => 'bar' + ) + )); + + // later on + $request->client()->callback_params('foo',FALSE); + +As with nested requests, callback_params will automatically be passed to subrequests if the callback returns a new Request object. If the callback returns a Response object, it is responsible for passing on any relevant parameters. + +#### Following redirects +The request client ships with a standard callback to automatically follow redirects - [Request_Client::on_header_location()]. This will recursively follow redirects that are specified with a Location header and a status code in 201, 301, 302, 303, 307. This behaviour is disabled by default, but can be enabled by passing a set of options to the Request's constructor: + + $request = Request::factory('http://example.com/redirectme', array( + 'follow' => TRUE)); + +[!!] If you define additional header callbacks of your own, you will need to include the 'Location' callback in your callbacks array. + +A number of options are available to control the behaviour of the [Request_Client] when following redirects. + +Option |Default |Function +-----------------|------------------------|--------- +follow | FALSE |Whether to follow redirects +follow_headers | array('Authorization') |The keys of headers that will be re-sent with the redirected request +strict_redirect | TRUE |Whether to use the original request method following to a 302 redirect (see below) + +[!!] HTTP/1.1 specifies that a 302 redirect should be followed using the original request method. However, the vast majority of clients and servers get this wrong, with 302 widely used for 'POST - 302 redirect - GET' patterns. By default, Kohana's client is fully compliant with the HTTP spec. If you need to interact with non-compliant third party sites you may need to set strict_redirect FALSE to force the client to switch to GET following a 302 response. + +You can easily alter this behaviour by configuring your own 'Location' header callback. + +## Request Cache Control + +You can cache requests for fast execution by passing a cache instance in as the second parameter of factory: + + $request = Request::factory('welcome', array('cache'=>Cache::instance())); + +TODO diff --git a/system/guide/kohana/routing.md b/system/guide/kohana/routing.md new file mode 100644 index 0000000..379c76d --- /dev/null +++ b/system/guide/kohana/routing.md @@ -0,0 +1,257 @@ +# Routing + +Kohana provides a very powerful routing system. In essence, routes provide an interface between the urls and your controllers and actions. With the correct routes you could make almost any url scheme correspond to almost any arrangement of controllers, and you could change one without impacting the other. + +As mentioned in the [Request Flow](flow) section, a request is handled by the [Request] class, which will look for a matching [Route] and load the appropriate controller to handle that request. + +[!!] It is important to understand that **routes are matched in the order they are added**, and as soon as a URL matches a route, routing is essentially "stopped" and *the remaining routes are never tried*. Because the default route matches almost anything, including an empty url, new routes must be place before it. + +## Creating routes + +If you look in `APPPATH/bootstrap.php` you will see the "default" route as follows: + + Route::set('default', '((/(/)))') + ->defaults(array( + 'controller' => 'Welcome', + 'action' => 'index', + )); + +[!!] The default route is simply provided as a sample, you can remove it and replace it with your own routes. + +So this creates a route with the name `default` that will match urls in the format of `((/(/)))`. + +Let's take a closer look at each of the parameters of [Route::set], which are `name`, `uri`, and an optional array `regex`. + +### Name + +The name of the route must be a **unique** string. If it is not it will overwrite the older route with the same name. The name is used for creating urls by reverse routing, or checking which route was matched. + +### URI + +The uri is a string that represents the format of urls that should be matched. The tokens surrounded with `<>` are *keys* and anything surrounded with `()` are *optional* parts of the uri. In Kohana routes, any character is allowed and treated literally aside from `()<>`. The `/` has no meaning besides being a character that must match in the uri. Usually the `/` is used as a static seperator but as long as the regex makes sense, there are no restrictions to how you can format your routes. + +Lets look at the default route again, the uri is `((/(/)))`. We have three keys or params: controller, action, and id. In this case, the entire uri is optional, so a blank uri would match and the default controller and action (set by defaults(), [covered below](#defaults)) would be assumed resulting in the `Controller_Welcome` class being loaded and the `action_index` method being called to handle the request. + +You can use any name you want for your keys, but the following keys have special meaning to the [Request] object, and will influence which controller and action are called: + + * **Directory** - The sub-directory of `classes/Controller` to look for the controller (\[covered below]\(#directory)) + * **Controller** - The controller that the request should execute. + * **Action** - The action method to call. + +### Regex + +The Kohana route system uses [perl compatible regular expressions](http://perldoc.perl.org/perlre.html) in its matching process. By default each key (surrounded by `<>`) will match `[^/.,;?\n]++` (or in english: anything that is not a slash, period, comma, semicolon, question mark, or newline). You can define your own patterns for each key by passing an associative array of keys and patterns as an additional third argument to Route::set. + +In this example, we have controllers in two directories, `admin` and `affiliate`. Because this route will only match urls that begin with `admin` or `affiliate`, the default route would still work for controllers in `classes/Controller`. + + Route::set('sections', '(/(/(/)))', + array( + 'directory' => '(admin|affiliate)' + )) + ->defaults(array( + 'controller' => 'Home', + 'action' => 'index', + )); + +You can also use a less restrictive regex to match unlimited parameters, or to ignore overflow in a route. In this example, the url `foobar/baz/and-anything/else_that/is-on-the/url` would be routed to `Controller_Foobar::action_baz()` and the `"stuff"` parameter would be `"and-anything/else_that/is-on-the/url"`. If you wanted to use this for unlimited parameters, you could [explode](http://php.net/manual/en/function.explode.php) it, or you just ignore the overflow. + + Route::set('default', '((/(/)))', array('stuff' => '.*')) + ->defaults(array( + 'controller' => 'Welcome', + 'action' => 'index', + )); + + +### Default values + +If a key in a route is optional (or not present in the route), you can provide a default value for that key by passing an associated array of keys and default values to [Route::defaults], chained after your [Route::set]. This can be useful to provide a default controller or action for your site, among other things. + +[!!] The `controller` and `action` key must always have a value, so they either need to be required in your route (not inside of parentheses) or have a default value provided. + +[!!] Kohana automatically converts controllers to follow the standard naming convention. For example /blog/view/123 would look for the controller Controller_Blog in classes/Controller/Blog.php and trigger the action_view() method on it. + +In the default route, all the keys are optional, and the controller and action are given a default. If we called an empty url, the defaults would fill in and `Controller_Welcome::action_index()` would be called. If we called `foobar` then only the default for action would be used, so it would call `Controller_Foobar::action_index()` and finally, if we called `foobar/baz` then neither default would be used and `Controller_Foobar::action_baz()` would be called. + +TODO: need an example here + +You can also use defaults to set a key that isn't in the route at all. + +TODO: example of either using directory or controller where it isn't in the route, but set by defaults + +### Directory + +## Route Filters + +In 3.3, you can specify advanced routing schemes by using filter callbacks. When you need to match a route based on more than just the URI of a request, for example, based on the method request (GET/POST/DELETE), a filter will allow you to do so. These filters will receive the `Route` object being tested, the currently matched `$params` array, and the `Request` object as the three parameters. Here's a simple example: + + Route::set('save-form', 'save') + ->filter(function($route, $params, $request) + { + if ($request->method() !== HTTP_Request::POST) + { + return FALSE; // This route only matches POST requests + } + }); + +Filters can also replace or alter the array of parameters: + + Route::set('rest-api', 'api/') + ->filter(function($route, $params, $request) + { + // Prefix the method to the action name + $params['action'] = strtolower($request->method()).'_'.$params['action']; + return $params; // Returning an array will replace the parameters + }) + ->defaults(array( + 'controller' => 'api', + )); + +If you are using php 5.2, you can still use any valid callback for this behavior: + + Route::set('testing', 'foo') + ->filter(array('Class', 'method_to_process_my_uri')); + +## Examples + +There are countless other possibilities for routes. Here are some more examples: + + /* + * Authentication shortcuts + */ + Route::set('auth', '', + array( + 'action' => '(login|logout)' + )) + ->defaults(array( + 'controller' => 'Auth' + )); + + /* + * Multi-format feeds + * 452346/comments.rss + * 5373.json + */ + Route::set('feeds', '(/).', + array( + 'user_id' => '\d+', + 'format' => '(rss|atom|json)', + )) + ->defaults(array( + 'controller' => 'Feeds', + 'action' => 'status', + )); + + /* + * Static pages + */ + Route::set('static', '.html', + array( + 'path' => '[a-zA-Z0-9_/]+', + )) + ->defaults(array( + 'controller' => 'Static', + 'action' => 'index', + )); + + /* + * You don't like slashes? + * EditGallery:bahamas + * Watch:wakeboarding + */ + Route::set('gallery', '():', + array( + 'controller' => '[A-Z][a-z]++', + 'action' => '[A-Z][a-z]++', + )) + ->defaults(array( + 'controller' => 'Slideshow', + )); + + /* + * Quick search + */ + Route::set('search', ':', array('query' => '.*')) + ->defaults(array( + 'controller' => 'Search', + 'action' => 'index', + )); + +## Request parameters + +The `directory`, `controller` and `action` can be accessed from the [Request] as public properties like so: + + // From within a controller: + $this->request->action(); + $this->request->controller(); + $this->request->directory(); + + // Can be used anywhere: + Request::current()->action(); + Request::current()->controller(); + Request::current()->directory(); + +All other keys specified in a route can be accessed via [Request::param()]: + + // From within a controller: + $this->request->param('key_name'); + + // Can be used anywhere: + Request::current()->param('key_name'); + +The [Request::param] method takes an optional second argument to specify a default return value in case the key is not set by the route. If no arguments are given, all keys are returned as an associative array. In addition, `action`, `controller` and `directory` are not accessible via [Request::param()]. + +For example, with the following route: + + Route::set('ads','ad/(/)') + ->defaults(array( + 'controller' => 'ads', + 'action' => 'index', + )); + +If a url matches the route, then `Controller_Ads::index()` will be called. You can access the parameters by using the `param()` method of the controller's [Request]. Remember to define a default value (via the second, optional parameter of [Request::param]) if you didn't in `->defaults()`. + + class Controller_Ads extends Controller { + public function action_index() + { + $ad = $this->request->param('ad'); + $affiliate = $this->request->param('affiliate',NULL); + } + + +## Where should routes be defined? + +The established convention is to either place your custom routes in the `MODPATH//init.php` file of your module if the routes belong to a module, or simply insert them into the `APPPATH/bootstrap.php` file (be sure to put them **above** the default route) if they are specific to the application. Of course, nothing stops you from including them from an external file, or even generating them dynamically. + +## A deeper look at how routes work + +TODO: talk about how routes are compiled + +## Creating URLs and links using routes + +Along with Kohana's powerful routing capabilities are included some methods for generating URLs for your routes' uris. You can always specify your uris as a string using [URL::site] to create a full URL like so: + + URL::site('admin/edit/user/'.$user_id); + +However, Kohana also provides a method to generate the uri from the route's definition. This is extremely useful if your routing could ever change since it would relieve you from having to go back through your code and change everywhere that you specified a uri as a string. Here is an example of dynamic generation that corresponds to the `feeds` route example from above: + + Route::get('feeds')->uri(array( + 'user_id' => $user_id, + 'action' => 'comments', + 'format' => 'rss' + )); + +Let's say you decided later to make that route definition more verbose by changing it to `feeds/(/).`. If you wrote your code with the above uri generation method you wouldn't have to change a single line! When a part of the uri is enclosed in parentheses and specifies a key for which there in no value provided for uri generation and no default value specified in the route, then that part will be removed from the uri. An example of this is the `(/)` part of the default route; this will not be included in the generated uri if an id is not provided. + +One method you might use frequently is the shortcut [Request::uri] which is the same as the above except it assumes the current route, directory, controller and action. If our current route is the default and the uri was `users/list`, we can do the following to generate uris in the format `users/view/$id`: + + $this->request->uri(array('action' => 'view', 'id' => $user_id)); + +Or if within a view, the preferable method is: + + Request::instance()->uri(array('action' => 'view', 'id' => $user_id)); + +TODO: examples of using html::anchor in addition to the above examples + +## Testing routes + +TODO: mention bluehawk's devtools module \ No newline at end of file diff --git a/system/guide/kohana/security.md b/system/guide/kohana/security.md new file mode 100644 index 0000000..2f90a04 --- /dev/null +++ b/system/guide/kohana/security.md @@ -0,0 +1 @@ +General security concerns, like using the Security class, CSRF, and a brief intro to XSS, database security, etc. Also mention the security features that Kohana provides, like cleaning globals. \ No newline at end of file diff --git a/system/guide/kohana/security/cookies.md b/system/guide/kohana/security/cookies.md new file mode 100644 index 0000000..3966f6c --- /dev/null +++ b/system/guide/kohana/security/cookies.md @@ -0,0 +1,3 @@ +Discuss security of cookies, like changing the encryption key in the config. + +Not sure why I'm linking to this: \ No newline at end of file diff --git a/system/guide/kohana/security/database.md b/system/guide/kohana/security/database.md new file mode 100644 index 0000000..e6190b7 --- /dev/null +++ b/system/guide/kohana/security/database.md @@ -0,0 +1,5 @@ +Discuss database security. + +How to avoid injection, etc. + +Not sure why I'm linking to this: \ No newline at end of file diff --git a/system/guide/kohana/security/deploying.md b/system/guide/kohana/security/deploying.md new file mode 100644 index 0000000..432e29f --- /dev/null +++ b/system/guide/kohana/security/deploying.md @@ -0,0 +1,31 @@ +Changes that should happen when you deploy. (Production) + +Security settings from: + + + + +## Setting up a production environment + +There are a few things you'll want to do with your application before moving into production. + +1. See the [Bootstrap page](bootstrap) in the docs. + This covers most of the global settings that would change between environments. + As a general rule, you should enable caching and disable profiling ([Kohana::init] settings) for production sites. + [Route::cache] can also help if you have a lot of routes. +2. Turn on APC or some kind of opcode caching. + This is the single easiest performance boost you can make to PHP itself. The more complex your application, the bigger the benefit of using opcode caching. + + /** + * Set the environment string by the domain (defaults to Kohana::DEVELOPMENT). + */ + Kohana::$environment = ($_SERVER['SERVER_NAME'] !== 'localhost') ? Kohana::PRODUCTION : Kohana::DEVELOPMENT; + /** + * Initialise Kohana based on environment + */ + Kohana::init(array( + 'base_url' => '/', + 'index_file' => FALSE, + 'profile' => Kohana::$environment !== Kohana::PRODUCTION, + 'caching' => Kohana::$environment === Kohana::PRODUCTION, + )); diff --git a/system/guide/kohana/security/encryption.md b/system/guide/kohana/security/encryption.md new file mode 100644 index 0000000..2d9252f --- /dev/null +++ b/system/guide/kohana/security/encryption.md @@ -0,0 +1,107 @@ +# Encryption + +Kohana supports built-in encryption and decryption via the [Encrypt] class, which is a convenient wrapper for the [Mcrypt library](http://www.php.net/mcrypt). + +To use the class, first start by ensuring you have the Mcrypt extension loaded to your PHP config. See the [Mcrypt Installation page](http://www.php.net/manual/en/mcrypt.installation.php) on php.net. The Mcrypt extension requires [libmcrypt](http://sourceforge.net/projects/mcrypt/files/). + +Next, copy the default config/encryption.php from system/config folder to your application/config folder. + +The default Encryption config file that ships with Kohana 3.2.x looks like this: + + array( + /** + * The following options must be set: + * + * string key secret passphrase + * integer mode encryption mode, one of MCRYPT_MODE_* + * integer cipher encryption cipher, one of the Mcrpyt cipher constants + */ + 'cipher' => MCRYPT_RIJNDAEL_128, + 'mode' => MCRYPT_MODE_NOFB, + ), + + ); + + +A couple of notes about the config. +First, you may have multiple first-level keys other than 'default' if you need to. +In this respect, the config file is similar to having multiple databases defined in your config/database.php file. +Second, notice there is no key provided. You need to add that. +It is strongly recommended that you choose a high-strength random key using the [pwgen linux program](http://linux.die.net/man/1/pwgen)... + + shell> pwgen 63 1 + trwQwVXX96TIJoKxyBHB9AJkwAOHixuV1ENZmIWyanI0j1zNgSVvqywy044Agaj + +...or by going to [GRC.com/passwords.htm](https://www.grc.com/passwords.htm). + +## Complete Config Example + +Here's a sample encryption configuration with three types of encryption defined. **If you copy this example, please change your keys!** + + array( + 'key' => 'trwQwVXX96TIJoKxyBHB9AJkwAOHixuV1ENZmIWyanI0j1zNgSVvqywy044Agaj', + 'cipher' => MCRYPT_RIJNDAEL_128, + 'mode' => MCRYPT_MODE_NOFB, + ), + 'blowfish' => array( + 'key' => '7bZJJkmNrelj5NaKoY6h6rMSRSmeUlJuTeOd5HHka5XknyMX4uGSfeVolTz4IYy', + 'cipher' => MCRYPT_BLOWFISH, + 'mode' => MCRYPT_MODE_ECB, + ), + 'tripledes' => array( + 'key' => 'a9hcSLRvA3LkFc7EJgxXIKQuz1ec91J7P6WNq1IaxMZp4CTj5m31gZLARLxI1jD', + 'cipher' => MCRYPT_3DES, + 'mode' => MCRYPT_MODE_CBC, + ), + ); + +You can view the available encryption ciphers and modes on your system by running... + + shell> php -r "print_r(get_defined_constants());" | grep MCRYPT + +For more information on Mcrypt ciphers, visit [php.net/mcrypt.ciphers](http://us3.php.net/manual/en/mcrypt.ciphers.php). + +## Basic Usage + +### Create an instance + +To use the Encryption class, obtain an instance of the Encrypt class by calling it's *instance* method, +optionally passing the desired configuration group. If you do not pass a config group to the instance method, +the default group will be used. + + $encrypt = Encrypt::instance('tripledes'); + +### Encoding Data + +Next, encode some data using the *encode* method: + + $encrypt = Encrypt::instance('tripledes'); + $encrypted_data = $encrypt->encode('Data to Encode'); + // $encrypted_data now contains pCD5Z6oVdb9hbLxxV+FgGrhwVzZuhQoH + +[!!] Raw encrypted strings usually won't print in a browser, and may not store properly in a VARCHAR or TEXT field. For this reason, Kohana's Encrypt class automatically calls base64_encode on encode, and base64_decode on decode, to prevent this problem. + +[!!] One word of caution. The length of the encoded data expands quite a bit, so be sure your database column is long enough to store the encrypted data. If even one character is truncated, the data will not be recoverable. + +### Decoding Data + +To decode some data, load it from the place you stored it (most likely your database) then pass it to the *decode* method: + + $encrypt = Encrypt::instance('tripledes'); + $decoded_string = $encrypt->decode($encrypted_data); + echo $decoded_string; + // prints 'Data to Encode' + +You can't know in advance what the encoded string will be, and it's not reproducible, either. +That is, you can encode the same value over and over, but you'll always obtain a different encoded version, +even without changing your key, cipher and mode. This is because Kohana adds some random entropy before encoding it with your value. +This ensures an attacker cannot easily discover your key and cipher, even given a collection of encoded values. \ No newline at end of file diff --git a/system/guide/kohana/security/validation.md b/system/guide/kohana/security/validation.md new file mode 100644 index 0000000..af67f22 --- /dev/null +++ b/system/guide/kohana/security/validation.md @@ -0,0 +1,266 @@ +# Validation + +*This page needs to be reviewed for accuracy by the development team. Better examples would be helpful.* + +Validation can be performed on any array using the [Validation] class. Labels and rules can be attached to a Validation object by the array key, called a "field name". + +labels +: A label is a human-readable version of the field name. + +rules +: A rule is a callback or closure used to decide whether or not to add an error to a field + +[!!] Note that any valid [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) can be used as a rule. + +Using `TRUE` as the field name when adding a rule will be applied to all named fields. + +Creating a validation object is done using the [Validation::factory] method: + + $object = Validation::factory($array); + +[!!] The `$object` object will be used for the rest of this tutorial. This tutorial will show you how to validate the registration of a new user. + +## Provided Rules + +Kohana provides a set of useful rules in the [Valid] class: + +Rule name | Function +------------------------- |------------------------------------------------- +[Valid::not_empty] | Value must be a non-empty value +[Valid::regex] | Match the value against a regular expression +[Valid::min_length] | Minimum number of characters for value +[Valid::max_length] | Maximum number of characters for value +[Valid::exact_length] | Value must be an exact number of characters +[Valid::email] | An email address is required +[Valid::email_domain] | Check that the domain of the email exists +[Valid::url] | Value must be a URL +[Valid::ip] | Value must be an IP address +[Valid::phone] | Value must be a phone number +[Valid::credit_card] | Require a credit card number +[Valid::date] | Value must be a date (and time) +[Valid::alpha] | Only alpha characters allowed +[Valid::alpha_dash] | Only alpha and hyphens allowed +[Valid::alpha_numeric] | Only alpha and numbers allowed +[Valid::digit] | Value must be an integer digit +[Valid::decimal] | Value must be a decimal or float value +[Valid::numeric] | Only numeric characters allowed +[Valid::range] | Value must be within a range +[Valid::color] | Value must be a valid HEX color +[Valid::matches] | Value matches another field value + +## Adding Rules + +All validation rules are defined as a field name, a method, a function (using the [PHP callback](http://php.net/callback) syntax) or [closure](http://php.net/manual/functions.anonymous.php), and an array of parameters: + + $object->rule($field, $callback, array($parameter1, $parameter2)); + +If no parameters are specified, the field value will be passed to the callback. The following two rules are equivalent: + + $object->rule($field, 'not_empty'); + $object->rule($field, 'not_empty', array(':value')); + +Rules defined in the [Valid] class can be added by using the method name alone. The following three rules are equivalent: + + $object->rule('number', 'phone'); + $object->rule('number', array('Valid', 'phone')); + $object->rule('number', 'Valid::phone'); + +### Adding Rules for multiple fields together + +To validate multiple fields together, you can do something like this: + + $object->rule('one', 'only_one', array(':validation', array('one', 'two'))); + $object->rule('two', 'only_one', array(':validation', array('one', 'two'))); + + public function only_one($validation, $fields) + { + // If more than 1 field is set, bail. + $matched = 0; + + foreach ($fields as $field) + { + if (isset($validation[$field])) + { + $matched++; + } + } + + if ($matched > 0) + { + // Add the error to all concerned fields + foreach ($fields as $field) + { + $validation->error($field, 'only_one'); + } + } + } + +## Binding Variables + +The [Validation] class allows you to bind variables to certain strings so that they can be used when defining rules. Variables are bound by calling the [Validation::bind] method. + + $object->bind(':model', $user_model); + // Future code will be able to use :model to reference the object + $object->rule('username', 'some_rule', array(':model')); + +By default, the validation object will automatically bind the following values for you to use as rule parameters: + +- `:validation` - references the validation object +- `:field` - references the field name the rule is for +- `:value` - references the value of the field the rule is for + +## Adding Errors + +The [Validation] class will add an error for a field if any of the rules associated to it return `FALSE`. This allows many built in PHP functions to be used as rules, like `in_array`. + + $object->rule('color', 'in_array', array(':value', array('red', 'green', 'blue'))); + +Rules added to empty fields will run, but returning `FALSE` will not automatically add an error for the field. In order for a rule to affect empty fields, you must add the error manually by calling the [Validation::error] method. In order to do this, you must pass the validation object to the rule. + + $object->rule($field, 'the_rule', array(':validation', ':field')); + + public function the_rule($validation, $field) + { + if (something went wrong) + { + $validation->error($field, 'the_rule'); + } + } + +[!!] `not_empty` and `matches` are the only rules that will run on empty fields and add errors by returning `FALSE`. + +## Example + +To start our example, we will perform validation on the HTTP POST data of the current request that contains user registration information: + +[!!] In Kohana controllers, we access `$this->request->post()` instead of `$_POST` for better request isolation. + + $object = Validation::factory($this->request->post()); + +Next we need to process the POST'ed information using [Validation]. To start, we need to add some rules: + + $object + ->rule('username', 'not_empty') + ->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD')) + ->rule('password', 'not_empty') + ->rule('password', 'min_length', array(':value', '6')) + ->rule('confirm', 'matches', array(':validation', 'confirm', 'password')) + ->rule('use_ssl', 'not_empty'); + +Any existing PHP function can also be used a rule. For instance, if we want to check if the user entered a proper value for the SSL question: + + $object->rule('use_ssl', 'in_array', array(':value', array('yes', 'no'))); + +Note that all array parameters must still be wrapped in an array! Without the wrapping array, `in_array` would be called as `in_array($value, 'yes', 'no')`, which would result in a PHP error. + +Any custom rules can be added using a [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback]: + + $object->rule('username', 'User_Model::unique_username'); + +The method `User_Model::unique_username()` would be defined similar to: + + public static function unique_username($username) + { + // Check if the username already exists in the database + return ! DB::select(array(DB::expr('COUNT(username)'), 'total')) + ->from('users') + ->where('username', '=', $username) + ->execute() + ->get('total'); + } + +[!!] Custom rules allow many additional checks to be reused for multiple purposes. These methods will almost always exist in a model, but may be defined in any class. + +# A Complete Example + +First, we need a [View] that contains the HTML form, which will be placed in `application/views/user/register.php`: + + + +

    Some errors were encountered, please check the details you entered.

    +
      + +
    • + + + +
      +
      +
      + +
      +
      +
      Passwords must be at least 6 characters long.
      +
      +
      + +
      +
      'Always', 'no' => 'Only when necessary'), $post['use_ssl']) ?>
      +
      For security, SSL is always used when making payments.
      +
      + + + + +[!!] This example uses the [Form] helper extensively. Using [Form] instead of writing HTML ensures that all of the form inputs will properly handle input that includes HTML characters. If you prefer to write the HTML yourself, be sure to use [HTML::chars] to escape user input. + +Next, we need a controller and action to process the registration, which will be placed in `application/classes/Controller/User.php`: + + class Controller_User extends Controller { + + public function action_register() + { + $user = Model::factory('user'); + + $validation = Validation::factory($this->request->post()) + ->rule('username', 'not_empty') + ->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD')) + ->rule('username', array($user, 'unique_username')) + + ->rule('password', 'not_empty') + ->rule('password', 'min_length', array(':value', 6)) + ->rule('confirm', 'matches', array(':validation', ':field', 'password')) + + ->rule('use_ssl', 'not_empty') + ->rule('use_ssl', 'in_array', array(':value', array('yes', 'no'))); + + if ($validation->check()) + { + // Data has been validated, register the user + $user->register($this->request->post()); + + // Always redirect after a successful POST to prevent refresh warnings + $this->redirect('user/profile', 302); + } + + // Validation failed, collect the errors + $errors = $validation->errors('user'); + + // Display the registration form + $this->response->body(View::factory('user/register')) + ->bind('post', $this->request->post()) + ->bind('errors', $errors); + } + + } + +We will also need a user model, which will be placed in `application/classes/Model/User.php`: + + class Model_User extends Model { + + public function register($array) + { + // Create a new user record in the database + $id = DB::insert(array_keys($array)) + ->values($array) + ->execute(); + + // Save the new user id to a cookie + cookie::set('user', $id); + + return $id; + } + + } + +That is it, we have a complete user registration example that properly checks user input! diff --git a/system/guide/kohana/security/xss.md b/system/guide/kohana/security/xss.md new file mode 100644 index 0000000..d1e1c11 --- /dev/null +++ b/system/guide/kohana/security/xss.md @@ -0,0 +1,17 @@ +# Cross-Site Scripting (XSS) Security + +*This page is not comprehensive and should not be considered a complete guide to XSS prevention.* + +The first step to preventing [XSS](http://wikipedia.org/wiki/Cross-Site_Scripting) attacks is knowing when you need to protect yourself. XSS can only be triggered when it is displayed within HTML content, sometimes via a form input or being displayed from database results. Any global variable that contains client information can be tainted. This includes `$_GET`, `$_POST`, and `$_COOKIE` data. + +## Prevention + +There are a few simple rules to follow to guard your application HTML against XSS. If you do not want HTML in a variable, use [strip_tags](http://php.net/strip_tags) to remove all unwanted HTML tags from a value. + +[!!] If you allow users to submit HTML to your application, it is highly recommended to use an HTML cleaning tool such as [HTML Purifier](http://htmlpurifier.org/) or [HTML Tidy](http://php.net/tidy). + +The second is to always escape data when inserting into HTML. The [HTML] class provides generators for many common tags, including script and stylesheet links, anchors, images, and email (mailto) links. Any untrusted content should be escaped using [HTML::chars]. + +## References + +* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) \ No newline at end of file diff --git a/system/guide/kohana/sessions.md b/system/guide/kohana/sessions.md new file mode 100644 index 0000000..d60e709 --- /dev/null +++ b/system/guide/kohana/sessions.md @@ -0,0 +1,167 @@ +# Sessions + +Kohana provides classes that make it easy to work with both cookies and sessions. At a high level both sessions and cookies provide the same functionality. They allow the developer to store temporary or persistent information about a specific client for later retrieval, usually to make something persistent between requests. + +Sessions should be used for storing temporary or private data. Very sensitive data should be stored using the [Session] class with the "database" or "native" adapters. When using the "cookie" adapter, the session should always be encrypted. + +[!!] For more information on best practices with session variables see [the seven deadly sins of sessions](http://lists.nyphp.org/pipermail/talk/2006-December/020358.html). + +## Storing, Retrieving, and Deleting Data + +[Cookie] and [Session] provide a very similar API for storing data. The main difference between them is that sessions are accessed using an object, and cookies are accessed using a static class. + +Accessing the session instance is done using the [Session::instance] method: + + // Get the session instance + $session = Session::instance(); + +When using sessions, you can also get all of the current session data using the [Session::as_array] method: + + // Get all of the session data as an array + $data = $session->as_array(); + +You can also use this to overload the `$_SESSION` global to get and set data in a way more similar to standard PHP: + + // Overload $_SESSION with the session data + $_SESSION =& $session->as_array(); + + // Set session data + $_SESSION[$key] = $value; + +### Storing Data + +Storing session or cookie data is done using the `set` method: + + // Set session data + $session->set($key, $value); + // Or + Session::instance()->set($key, $value); + + // Store a user id + $session->set('user_id', 10); + +### Retrieving Data + +Getting session or cookie data is done using the `get` method: + + // Get session data + $data = $session->get($key, $default_value); + + // Get the user id + $user = $session->get('user_id'); + +### Deleting Data + +Deleting session or cookie data is done using the `delete` method: + + // Delete session data + $session->delete($key); + + + // Delete the user id + $session->delete('user_id'); + +## Session Configuration + +Always check these settings before making your application live, as many of them will have a direct affect on the security of your application. + +## Session Adapters + +When creating or accessing an instance of the [Session] class you can decide which session adapter or driver you wish to use. The session adapters that are available to you are: + +Native +: Stores session data in the default location for your web server. The storage location is defined by [session.save_path](http://php.net/manual/session.configuration.php#ini.session.save-path) in `php.ini` or defined by [ini_set](http://php.net/ini_set). + +Database +: Stores session data in a database table using the [Session_Database] class. Requires the [Database] module to be enabled. + +Cookie +: Stores session data in a cookie using the [Cookie] class. **Sessions will have a 4KB limit when using this adapter, and should be encrypted.** + +The default adapter can be set by changing the value of [Session::$default]. The default adapter is "native". + +To access a Session using the default adapter, simply call [Session::instance()]. To access a Session using something other than the default, pass the adapter name to `instance()`, for example: `Session::instance('cookie')` + + +### Session Adapter Settings + +You can apply configuration settings to each of the session adapters by creating a session config file at `APPPATH/config/session.php`. The following sample configuration file defines all the settings for each adapter: + +[!!] As with cookies, a "lifetime" setting of "0" means that the session will expire when the browser is closed. + + return array( + 'native' => array( + 'name' => 'session_name', + 'lifetime' => 43200, + ), + 'cookie' => array( + 'name' => 'cookie_name', + 'encrypted' => TRUE, + 'lifetime' => 43200, + ), + 'database' => array( + 'name' => 'cookie_name', + 'encrypted' => TRUE, + 'lifetime' => 43200, + 'group' => 'default', + 'table' => 'table_name', + 'columns' => array( + 'session_id' => 'session_id', + 'last_active' => 'last_active', + 'contents' => 'contents' + ), + 'gc' => 500, + ), + ); + +#### Native Adapter + +Type | Setting | Description | Default +----------|-----------|---------------------------------------------------|----------- +`string` | name | name of the session | `"session"` +`integer` | lifetime | number of seconds the session should live for | `0` + +#### Cookie Adapter + +Type | Setting | Description | Default +----------|-----------|---------------------------------------------------|----------- +`string` | name | name of the cookie used to store the session data | `"session"` +`boolean` | encrypted | encrypt the session data using [Encrypt]? | `FALSE` +`integer` | lifetime | number of seconds the session should live for | `0` + +#### Database Adapter + +Type | Setting | Description | Default +----------|-----------|---------------------------------------------------|----------- +`string` | group | [Database::instance] group name | `"default"` +`string` | table | table name to store sessions in | `"sessions"` +`array` | columns | associative array of column aliases | `array` +`integer` | gc | 1:x chance that garbage collection will be run | `500` +`string` | name | name of the cookie used to store the session data | `"session"` +`boolean` | encrypted | encrypt the session data using [Encrypt]? | `FALSE` +`integer` | lifetime | number of seconds the session should live for | `0` + +##### Table Schema + +You will need to create the session storage table in the database. This is the default schema: + + CREATE TABLE `sessions` ( + `session_id` VARCHAR(24) NOT NULL, + `last_active` INT UNSIGNED NOT NULL, + `contents` TEXT NOT NULL, + PRIMARY KEY (`session_id`), + INDEX (`last_active`) + ) ENGINE = MYISAM; + +##### Table Columns + +You can change the column names to match an existing database schema when connecting to a legacy session table. The default value is the same as the key value. + +session_id +: the name of the "id" column + +last_active +: UNIX timestamp of the last time the session was updated + +contents +: session data stored as a serialized string, and optionally encrypted \ No newline at end of file diff --git a/system/guide/kohana/tips.md b/system/guide/kohana/tips.md new file mode 100644 index 0000000..2d6b733 --- /dev/null +++ b/system/guide/kohana/tips.md @@ -0,0 +1,41 @@ +# Tips and Common Mistakes + +This is a collection of tips and common mistakes or errors you may encounter. + +## Never edit the `system` folder! + +You should (almost) never edit the system folder. Any change you want to make to files in system and modules can be made via the [cascading filesystem](files) and [transparent extension](extension) and won't break when you try to update your Kohana version. + +## Don't try and use one route for everything + +Kohana 3 [routes](routing) are very powerful and flexible, don't be afraid to use as many as you need to make your app function the way you want! + +## Files not found on some systems + +As of Kohana 3.3, classes are autoloaded using the case-sensitive PSR-0 autoloader. This means that using the class Foo {} with a file in classes/foo.php will work on case-insensitive file systems (such as the default HFS+ FS used in Mac OS X) but will fail when used on a case-sensitive FS (typical on many production Linux servers). + +## Handling lots of routes + +Sometimes your application is sufficiently complex that you have many routes and it becomes unmanageable to put them all in bootstrap.php. If this is the case, simply make a `routes.php` file in APPPATH and require that in your bootstrap: `require_once APPPATH.'routes'.EXT;` + +## Reflection_Exception + +If you get a Reflection_Exception when setting up your site, it is almost certainly because your [Kohana::init] 'base_url' setting is wrong. If your base url is correct something is probably wrong with your [routes](routing). + + ReflectionException [ -1 ]: Class controller_ does not exist + // where is part of the url you entered in your browser + +### Solution {#reflection-exception-solution} + +Set your [Kohana::init] 'base_url' to the correct setting. The base url should be the path to your index.php file relative to the webserver document root. + +## ORM/Session __sleep() bug + +There is a bug in php which can corrupt your session after a fatal error. A production server shouldn't have uncaught fatal errors, so this bug should only happen during development, when you do something stupid and cause a fatal error. On the next page load you will get a database connection error, then all subsequent page loads will display the following error: + + ErrorException [ Notice ]: Undefined index: id + MODPATH/orm/classes/kohana/orm.php [ 1308 ] + +### Solution {#orm-session-sleep-solution} + +To fix this, clear your cookies for that domain to reset your session. This should never happen on a production server, so you won't have to explain to your clients how to clear their cookies. You can see the [discussion on this issue](http://dev.kohanaframework.org/issues/3242) for more details. diff --git a/system/guide/kohana/tutorials.md b/system/guide/kohana/tutorials.md new file mode 100644 index 0000000..c921bcd --- /dev/null +++ b/system/guide/kohana/tutorials.md @@ -0,0 +1,17 @@ +# Tutorials + +## Tutorials in this guide + +## Tutorials written elsewhere + +### Ellisgl's KO3 tutorial on dealtaker.com: + +1. [Install and Basic Usage](http://www.dealtaker.com/blog/2009/11/20/kohana-php-3-0-ko3-tutorial-part-1/) +2. [Views](http://www.dealtaker.com/blog/2009/12/07/kohana-php-3-0-ko3-tutorial-part-2/) +3. [Controllers](http://www.dealtaker.com/blog/2009/12/30/kohana-php-3-0-ko3-tutorial-part-3/) +4. [Models](http://www.dealtaker.com/blog/2010/02/01/kohana-php-3-0-ko3-tutorial-part-4/) +5. [Subrequests](http://www.dealtaker.com/blog/2010/02/25/kohana-php-3-0-ko3-tutorial-part-5/) +6. [Routes](http://www.dealtaker.com/blog/2010/03/03/kohana-php-3-0-ko3-tutorial-part-6/) +7. [Helpers](http://www.dealtaker.com/blog/2010/03/26/kohana-php-3-0-ko3-tutorial-part-7/) +8. [Modules](http://www.dealtaker.com/blog/2010/04/30/kohana-php-3-0-ko3-tutorial-part-8/) +9. [Vendor Libraries](http://www.dealtaker.com/blog/2010/06/02/kohana-php-3-0-ko3-tutorial-part-9/) \ No newline at end of file diff --git a/system/guide/kohana/tutorials/clean-urls.md b/system/guide/kohana/tutorials/clean-urls.md new file mode 100644 index 0000000..e5a60c4 --- /dev/null +++ b/system/guide/kohana/tutorials/clean-urls.md @@ -0,0 +1,93 @@ +# Clean URLs + +Removing `index.php` from your urls. + +To keep your URLs clean, you will probably want to be able to access your app without having `/index.php/` in the URL. There are two steps to remove `index.php` from the URL. + +1. Edit the bootstrap file +2. Set up rewriting + +## 1. Configure Bootstrap + +The first thing you will need to change is the `index_file` setting of [Kohana::init] to false: + + Kohana::init(array( + 'base_url' => '/myapp/', + 'index_file' => FALSE, + )); + +This change will make it so all of the links generated using [URL::site], [URL::base], and [HTML::anchor] will no longer include "index.php" in the URL. All generated links will start with `/myapp/` instead of `/myapp/index.php/`. + +## 2. URL Rewriting + +Enabling rewriting is done differently, depending on your web server. + +Rewriting will make it so urls will be passed to index.php. + +## Apache + +Rename `example.htaccess` to only `.htaccess` and alter the `RewriteBase` line to match the `base_url` setting from your [Kohana::init] + + RewriteBase /myapp/ + +The rest of the `.htaccess file` rewrites all requests through index.php, unless the file exists on the server (so your css, images, favicon, etc. are still loaded like normal). In most cases, you are done! + +### 404 errors + +If you get a "404 Not Found" error when trying to view a page then it's likely Apache is not configured to read the `.htaccess` file. + +In the main apache configuration file (usually `httpd.conf`), or in the virtual server configuration file, check that the `AccessFileName` directive is set to `.htaccess` and the `AllowOverride` directive is set to `All`. + + AccessFileName .htaccess + + + AllowOverride All + + + +### Failed! + +If you get a "Internal Server Error" or "No input file specified" error, try changing: + + RewriteRule ^(?:application|modules|system)\b - [F,L] + +Instead, we can try a slash: + + RewriteRule ^(application|modules|system)/ - [F,L] + +If that doesn't work, try changing: + + RewriteRule .* index.php/$0 [PT] + +To something more simple: + + RewriteRule .* index.php [PT] + +### Still Failed! + +If you are still getting errors, check to make sure that your host supports URL `mod_rewrite`. If you can change the Apache configuration, add these lines to the configuration, usually `httpd.conf`: + + + Order allow,deny + Allow from all + AllowOverride All + + +You should also check your Apache logs to see if they can shed some light on the error. + +## NGINX + +It is hard to give examples of nginx configuration, but here is a sample for a server: + + location / { + index index.php index.html index.htm; + try_files $uri index.php; + } + + location = index.php { + include fastcgi.conf; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + } + +If you are having issues getting this working, enable debug level logging in nginx and check the access and error logs. diff --git a/system/guide/kohana/tutorials/error-pages.md b/system/guide/kohana/tutorials/error-pages.md new file mode 100644 index 0000000..adb027f --- /dev/null +++ b/system/guide/kohana/tutorials/error-pages.md @@ -0,0 +1,99 @@ +# Custom Error Pages + +Custom error pages allow you to display a friendly error message to users, rather than the standard Kohana stack trace. + +## Prerequisites + +1. You will need `'errors' => TRUE` passed to [Kohana::init]. This will convert PHP-errors into exceptions which are easier to handle (The default value is `TRUE`). +2. Custom error pages will only be used to handle throw [HTTP_Exception]'s. If you simply set a status of, for example, 404 via [Respose::status] the custom page will not be used. + +## Extending the HTTP_Exception classes + +Handling [HTTP_Exception]'s in Kohana has become easier with the changes introduced in 3.3. + +For each [HTTP_Exception] class we can individually override the generation of the [Response] instance. + +[!!] Note: We can also use HMVC to issue a sub-request to another page rather than generating the [Response] in the [HTTP_Exception] itself. + +For example, to handle 404 pages we can do this in APPPATH/classes/HTTP/Exception/404.php: + + class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 { + + /** + * Generate a Response for the 404 Exception. + * + * The user should be shown a nice 404 page. + * + * @return Response + */ + public function get_response() + { + $view = View::factory('errors/404'); + + // Remembering that `$this` is an instance of HTTP_Exception_404 + $view->message = $this->getMessage(); + + $response = Response::factory() + ->status(404) + ->body($view->render()); + + return $response; + } + } + +Another example, this time to handle 401 Unauthorized errors (aka "Not Logged In") we can do this in APPPATH/classes/HTTP/Exception/401.php: + + class HTTP_Exception_401 extends Kohana_HTTP_Exception_401 { + + /** + * Generate a Response for the 401 Exception. + * + * The user should be redirect to a login page. + * + * @return Response + */ + public function get_response() + { + $response = Response::factory() + ->status(401) + ->headers('Location', URL::site('account/login')); + + return $response; + } + } + +Finally, to override the default [Response] for all [HTTP_Exception]'s without a more specific override we can do this in APPPATH/classes/HTTP/Exception.php: + + class HTTP_Exception extends Kohana_HTTP_Exception { + + /** + * Generate a Response for all Exceptions without a more specific override + * + * The user should see a nice error page, however, if we are in development + * mode we should show the normal Kohana error page. + * + * @return Response + */ + public function get_response() + { + // Lets log the Exception, Just in case it's important! + Kohana_Exception::log($this); + + if (Kohana::$environment >= Kohana::DEVELOPMENT) + { + // Show the normal Kohana error page. + return parent::get_response(); + } + else + { + // Generate a nicer looking "Oops" page. + $view = View::factory('errors/default'); + + $response = Response::factory() + ->status($this->getCode()) + ->body($view->render()); + + return $response; + } + } + } \ No newline at end of file diff --git a/system/guide/kohana/tutorials/git.md b/system/guide/kohana/tutorials/git.md new file mode 100644 index 0000000..6487ed9 --- /dev/null +++ b/system/guide/kohana/tutorials/git.md @@ -0,0 +1,143 @@ +# Creating a New Application + +[!!] The following examples assume that your web server is already set up, and you are going to create a new application at . + +Using your console, change to the empty directory `gitorial` and run `git init`. This will create the bare structure for a new git repository. + +Next, we will create a [submodule](http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html) for the `system` directory. Go to and copy the "Clone URL": + +![Github Clone URL](http://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png) + +Now use the URL to create the submodule for `system`: + + git submodule add git://github.com/kohana/core.git system + +[!!] This will create a link to the current development version of the next stable release. The development version should almost always be safe to use, have the same API as the current stable download with bugfixes applied. + +Now add whatever submodules you need. For example, if you need the [Database] module: + + git submodule add git://github.com/kohana/database.git modules/database + +After submodules are added, they must be initialized: + + git submodule init + +Now that the submodules are added, you can commit them: + + git commit -m 'Added initial submodules' + +Next, create the application directory structure. This is the bare minimum required: + + mkdir -p application/classes/{Controller,Model} + mkdir -p application/{config,views} + mkdir -m 0777 -p application/{cache,logs} + +If you run `find application` you should see this: + + application + application/cache + application/config + application/classes + application/classes/Controller + application/classes/Model + application/logs + application/views + +We don't want git to track log or cache files, so add a `.gitignore` file to each of the directories. This will ignore all non-hidden files: + + echo '[^.]*' > application/{logs,cache}/.gitignore + +[!!] Git ignores empty directories, so adding a `.gitignore` file also makes sure that git will track the directory, but not the files within it. + +Now we need the `index.php` and `bootstrap.php` files: + + wget https://github.com/kohana/kohana/raw/3.3/master/index.php --no-check-certificate + wget https://github.com/kohana/kohana/raw/3.3/master/application/bootstrap.php --no-check-certificate -O application/bootstrap.php + +Commit these changes too: + + git add application + git commit -m 'Added initial directory structure' + +That's all there is to it. You now have an application that is using Git for versioning. + +## Adding Submodules +To add a new submodule complete the following steps: + +1. run the following code - git submodule add repository path for each new submodule e.g.: + + git submodule add git://github.com/shadowhand/sprig.git modules/sprig + +2. then init and update the submodules: + + git submodule init + git submodule update + +## Updating Submodules + +At some point you will probably also want to upgrade your submodules. To update all of your submodules to the latest `HEAD` version: + + git submodule foreach 'git checkout 3.3/master && git pull origin 3.3/master' + +To update a single submodule, for example, `system`: + + cd system + git checkout 3.3/master + git pull origin 3.3/master + cd .. + git add system + git commit -m 'Updated system to latest version' + +If you want to update a single submodule to a specific commit: + + cd modules/database + git pull origin 3.3/master + git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b + cd ../.. + git add database + git commit -m 'Updated database module' + +Note that you can also check out the commit at a tagged official release point, for example: + + git checkout v3.3.0 + +Simply run `git tag` without arguments to get a list of all tags. + +## Removing Submodules +To remove a submodule that is no longer needed complete the following steps: + +1. open .gitmodules and remove the reference to the to submodule + It will look something like this: + + [submodule "modules/auth"] + path = modules/auth + url = git://github.com/kohana/auth.git + +2. open .git/config and remove the reference to the to submodule\\ + + [submodule "modules/auth"] + url = git://github.com/kohana/auth.git + +3. run git rm --cached path/to/submodule, e.g. + + git rm --cached modules/auth + +**Note:** Do not put a trailing slash at the end of path. If you put a trailing slash at the end of the command, it will fail. + +## Updating Remote Repository URL + +During the development of a project, the source of a submodule may change for any reason (you've created your own fork, the server URL changed, the repository name or path changed, etc...) and you'll have to update those changes. To do so, you'll need to perform the following steps: + +1. edit the .gitmodules file, and change the URL for the submodules which changed. + +2. in your source tree's root run: + + git submodule sync + +3. run `git init` to update the project's repository configuration with the new URLs: + + git submodule init + +And it's done, now you can continue pushing and pulling your submodules with no problems. + +Source: http://jtrancas.wordpress.com/2011/02/06/git-submodule-location/ \ No newline at end of file diff --git a/system/guide/kohana/tutorials/hello-world.md b/system/guide/kohana/tutorials/hello-world.md new file mode 100644 index 0000000..0d21409 --- /dev/null +++ b/system/guide/kohana/tutorials/hello-world.md @@ -0,0 +1,106 @@ +# Hello, World + +Just about every framework ever written has some kind of hello world example included, so it'd be pretty rude of us to break this tradition! + +We'll start out by creating a very very basic hello world, and then we'll expand it to follow MVC principles. + +## Bare bones + +First off we have to make a controller that Kohana can use to handle a request. + +Create the file `application/classes/Controller/Hello.php` in your application folder and fill it out like so: + + template->message = 'hello, world!'; + } + } + +`extends Controller_Template` +: We're now extending the template controller, it makes it more convenient to use views within our controller. + +`public $template = 'site';` +: The template controller needs to know what template you want to use. It'll automatically load the view defined in this variable and assign the view object to it. + +`$this->template->message = 'hello, world!';` +: `$this->template` is a reference to the view object for our site template. What we're doing here is assigning a variable called "message", with a value of "hello, world!" to the view. + +Now lets try running our code... + +![Hello, World!](hello_world_2_error.png "Hello, World!") + +For some reason Kohana's thrown a wobbly and isn't showing our amazing message. + +If we look at the error message we can see that the View library wasn't able to find our site template, probably because we haven't made it yet – *doh*! + +Let's go and make the view file `application/views/site.php` for our message: + + + + We've got a message for you! + + + +

      +

      We just wanted to say it! :)

      + + + +If we refresh the page then we can see the fruits of our labour: + +![hello, world! We just wanted to say it!](hello_world_2.png "hello, world! We just wanted to say it!") + +## Stage 3 – Profit! + +In this tutorial you've learnt how to create a controller and use a view to separate your logic from your display. + +This is obviously a very basic introduction to working with Kohana and doesn't even scrape the potential you have when developing applications with it. \ No newline at end of file diff --git a/system/guide/kohana/tutorials/library-kohana.md b/system/guide/kohana/tutorials/library-kohana.md new file mode 100644 index 0000000..91fa495 --- /dev/null +++ b/system/guide/kohana/tutorials/library-kohana.md @@ -0,0 +1,219 @@ +# Importing Kohana as a Library + +If you're working with an existing codebase it's often difficult to modernise the code as it would mean a complete rewrite and there's rarely the time. An alternative is to improve the codebase incrementally as best you can, gradually outsourcing code to external libraries to reduce the amount of old code there is to maintain. + +This tutorial describes how to include the Kohana PHP framework into existing PHP applications, without having to use the routing and HMVC request handling features. + +[!!] The code modified in this tutorial was copied from Kohana version 3.1.x. You may need to update it to work with future releases. + +In normal usage of the Kohana framework, the `index.php` file acts as the request handler; it sets up the environment, loads the system configuration, and then handles the request (see [Request Flow](flow)). +We'll walk you through the steps required to create a file we'll call `include.php` which will allow you to include Kohana from exiting PHP applications. + +## Demo application + +The following file will serve as our (insultingly simple) demo application for this tutorial. + +### File: `demo.php` + +~~~ + + + + Demo page + + + + + +~~~ + +## Install Kohana + +[Download and install the Kohana framework](install); from this point on, we'll be referring to the location of the Kohana libraries as the `kohana` directory. + +## Create a common setup file + +Since `index.php` and `include.php` will duplicate a lot of code, we're going to move that code to a third file, `common.php`. The bulk of the code is unchanged; we've changed the install check to exit rather than return after rendering, and removed the request execution. + +The new file creates the initial request object, rather than fully executing the request, so that, if you do define routes, the `Request::$initial` variable will be set up correctly. + +### File: `kohana/common.php` + +~~~ + = 5.3, it is recommended to disable + * deprecated notices. Disable with: E_ALL & ~E_DEPRECATED + */ + error_reporting(E_ALL | E_STRICT); + + /** + * End of standard configuration! Changing any of the code below should only be + * attempted by those with a working knowledge of Kohana internals. + * + * @link http://kohanaframework.org/guide/using.configuration + */ + + // Set the full path to the docroot + define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR); + + // Make the application relative to the docroot, for symlink'd index.php + if ( ! is_dir($application) AND is_dir(DOCROOT.$application)) + $application = DOCROOT.$application; + + // Make the modules relative to the docroot, for symlink'd index.php + if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules)) + $modules = DOCROOT.$modules; + + // Make the system relative to the docroot, for symlink'd index.php + if ( ! is_dir($system) AND is_dir(DOCROOT.$system)) + $system = DOCROOT.$system; + + // Define the absolute paths for configured directories + define('APPPATH', realpath($application).DIRECTORY_SEPARATOR); + define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR); + define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR); + + // Clean up the configuration vars + unset($application, $modules, $system); + + if (file_exists('install'.EXT)) + { + // Load the installation check + include 'install'.EXT; + exit; // Changes were made here + } + + /** + * Define the start time of the application, used for profiling. + */ + if ( ! defined('KOHANA_START_TIME')) + { + define('KOHANA_START_TIME', microtime(TRUE)); + } + + /** + * Define the memory usage at the start of the application, used for profiling. + */ + if ( ! defined('KOHANA_START_MEMORY')) + { + define('KOHANA_START_MEMORY', memory_get_usage()); + } + + // Bootstrap the application + require APPPATH.'bootstrap'.EXT; + + /** + * Instantiate the request object. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. + * If no source is specified, the URI will be automatically detected. + */ + Request::factory(); // Changes were made here +~~~ + +## Alter Kohana's `index.php` + +Having moved most of the code from Kohana's `index.php` to `common.php` the new `kohana/index.php` contains only this: + +### File: `kohana/index.php` + +~~~ + execute() + ->execute() + ->send_headers(TRUE) + ->body(); +~~~ + +## Create the include file + +Our `include.php` file is also pretty simple. The try-catch clause is needed because if the request matches no routes Kohana will throw an `HTTP_Exception_404` exception. + +### File: `kohana/include.php` + +~~~ + + + + Demo page + + + +
      + +
      + + + +~~~ \ No newline at end of file diff --git a/system/guide/kohana/tutorials/sharing-kohana.md b/system/guide/kohana/tutorials/sharing-kohana.md new file mode 100644 index 0000000..193998a --- /dev/null +++ b/system/guide/kohana/tutorials/sharing-kohana.md @@ -0,0 +1,54 @@ +# Sharing Kohana + +Kohana follows a [front controller pattern](http://en.wikipedia.org/wiki/Front_Controller_pattern "Front Controller pattern") (which means that all requests are sent to `index.php`) and as such the [filesystem](files) is very configurable. Inside of `index.php` you can change the `$application`, `$modules`, and `$system` paths. + +[!!] There is a security check at the top of every Kohana file to prevent it from being accessed without using the front controller. Also, the `.htaccess` file should protect those folders as well. Moving the application, modules, and system directories to a location that cannot be accessed via the web can add another layer of security, but is optional. + +The `$application` variable lets you set the directory that contains your application files. By default, this is `application`. The `$modules` variable lets you set the directory that contains module files. The `$system` variable lets you set the directory that contains the default Kohana files. You can move these three directories anywhere. + +For instance, by default the directories are set up like this: + + www/ + index.php + application/ + modules/ + system/ + +You could move the directories out of the web root so they look like this: + + application/ + modules/ + system/ + www/ + index.php + +Then you would need to change the settings in `index.php` to be: + + $application = '../application'; + $modules = '../modules'; + $system = '../system'; + +## Sharing system and modules + +To take this a step further, we could point several Kohana apps to the same system and modules folders. For example (and this is just an example, you could arrange these anyway you want): + + apps/ + foobar/ + application/ + www/ + bazbar/ + application/ + www/ + kohana/ + 3.0.6/ + 3.0.7/ + 3.0.8/ + modules/ + +And you would need to change the settings in `index.php` to be: + + $application = '../application'; + $system = '../../../kohana/3.0.6'; + $modules = '../../../kohana/modules'; + +With this method each app can point to a central copy of Kohana, and when you add a new version, allow you to quickly update the apps by editing their respective `index.php` files. \ No newline at end of file diff --git a/system/guide/kohana/tutorials/simple-mvc.md b/system/guide/kohana/tutorials/simple-mvc.md new file mode 100644 index 0000000..b3a475d --- /dev/null +++ b/system/guide/kohana/tutorials/simple-mvc.md @@ -0,0 +1 @@ +Simple example of controller model and view working together. \ No newline at end of file diff --git a/system/guide/kohana/tutorials/templates.md b/system/guide/kohana/tutorials/templates.md new file mode 100644 index 0000000..4bfd9c2 --- /dev/null +++ b/system/guide/kohana/tutorials/templates.md @@ -0,0 +1,7 @@ +Making a template driven site. + + + + + + \ No newline at end of file diff --git a/system/guide/kohana/tutorials/translation.md b/system/guide/kohana/tutorials/translation.md new file mode 100644 index 0000000..12f7cfc --- /dev/null +++ b/system/guide/kohana/tutorials/translation.md @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/system/guide/kohana/upgrading.md b/system/guide/kohana/upgrading.md new file mode 100644 index 0000000..5c14994 --- /dev/null +++ b/system/guide/kohana/upgrading.md @@ -0,0 +1,100 @@ +# Migrating from 3.2.x + +## HVMC Isolation + +HVMC Sub-request isolation has been improved to prevent exceptions leaking from this inner to the outer request. If you were previously catching any exceptions from sub-requests, you should now be checking the [Response] object returned from [Request::execute]. + +## HTTP Exceptions + +The use of HTTP Exceptions is now encouraged over manually setting the [Response] status to, for example, '404'. This allows for easier custom error pages (detailed below); + +The full list of supported codes can be seen in the SYSPATH/classes/HTTP/Exception/ folder. + +Syntax: + + throw HTTP_Exception::factory($code, $message, array $variables, Exception $previous); + +Examples: + + // Page Not Found + throw HTTP_Exception::factory(404, 'The requested URL :uri was not found on this server.', array( + ':uri' => $this->request->uri(), + )); + + // Unauthorized / Login Requied + throw HTTP_Exception::factory(401)->authenticate('Basic realm="MySite"'); + + // Forbidden / Permission Deined + throw HTTP_Exception::factory(403); + +## Redirects (HTTP 300, 301, 302, 303, 307) + +Redirects are no longer issued against the [Request] object. The new syntax from inside a controller is: + + $this->redirect('http://www.google.com', 302); + +or from outside a controller: + + HTTP::redirect('http://www.google.com', 302); + +## Custom Error Pages (HTTP 500, 404, 403, 401 etc) + +Custom error pages are now easier than ever to implement, thanks to some of the changes brought about by the HVMC and Redirect changes above. + +See [Custom Error Pages](tutorials/error-pages) for more details. + +## Browser cache checking (ETags) + +The Response::check_cache method has moved to [HTTP::check_cache], with an alias at [Controller::check_cache]. Previously, this method would be used from a controller like this: + + $this->response->check_cache(sha1('my content'), Request $this->request); + +Now, there are two options for using the method: + + $this->check_cache(sha1('my content')); + +which is an alias for: + + HTTP::check_cache($this->request, $this->response, sha1('my content')); + +## PSR-0 support (file/class naming conventions) + +With the introduction of [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) support, the autoloading of classes is case sensitive. Now, the file (and folder) names must match the class name exactly. + +Examples: + + Kohana_Core + +would be located in + + classes/Kohana/Core.php + +and + + Kohana_HTTP_Header + +would be located in + + classes/Kohana/HTTP/Header.php + +This also affects dynamically named classes such as drivers and ORMs. So for example, in the database config using `'mysql'` as the type instead of `'MySQL'` would throw a class not found error. + +## Query Builder Identifier Escaping + +The query builder will no longer detect columns like `COUNT("*")`. Instead, you will need to use `DB::expr()` any time you need an unescaped column. For example: + + DB::select(DB::expr('COUNT(*)'))->from('users')->execute() + +## Route Filters + +In `3.3.0`, you can no longer pass a callback to `Route::uri()`. Instead, we've added the ability to define one or more filters which will be able to decide if the route matches and will also allow you to change any of the parameters. These filters will receive the `Route` object being tested, the currently matched `$params` array, and the `Request` object as the three parameters. + + Route::set('route-name', 'some/uri/') + ->filter(function($route, $params, $request) { + // Returning FALSE will make this route not match + // Returning an array will replace the $params sent to the controller + }); + +These filters can be used for things like prepending the request method to the action, checking if a resource exists before matching the route, or any other logic that the URI alone cannot provide. You can add as many filters as needed so it's useful to keep filters as small as possible to make them reusable. + +See [Routing](routing#route-filters) for more details. \ No newline at end of file diff --git a/system/i18n/en.php b/system/i18n/en.php new file mode 100644 index 0000000..eda5447 --- /dev/null +++ b/system/i18n/en.php @@ -0,0 +1,3 @@ + 'Español', + 'Hello, world!' => '¡Hola, mundo!', +); \ No newline at end of file diff --git a/system/i18n/fr.php b/system/i18n/fr.php new file mode 100644 index 0000000..a2e86fa --- /dev/null +++ b/system/i18n/fr.php @@ -0,0 +1,7 @@ + 'Français', + 'Hello, world!' => 'Bonjour, monde!', +); \ No newline at end of file diff --git a/system/media/guide/kohana/cascading_filesystem.png b/system/media/guide/kohana/cascading_filesystem.png new file mode 100644 index 0000000000000000000000000000000000000000..52a3576df98ae30b214ac4ad2f209dfe2d8690ef GIT binary patch literal 61164 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 literal 0 HcmV?d00001 diff --git a/system/media/guide/kohana/hello_world_2.png b/system/media/guide/kohana/hello_world_2.png new file mode 100644 index 0000000000000000000000000000000000000000..9eb10f5676d980b0c689a531e822a542b6608224 GIT binary patch literal 6681 zcma)hWl$VU)9n)6-JQkBLI@BbK#;{cIPqVVg0!Tz&(e{R zw;{>EvPs!=G{&@sI*Eov3p7QOCnDGr9`uZ2ceYYnH>?MOoTRGN0kjnNEAX|3_nL9K**XZr+_P3qfWKZq6TN zn6J+Eh?#a19t(bqCZ{1ty0AQyA?=La&?$ZUzpG$Zk}mY^;ae^>g08FO$<+jrCK(wo zB5&6Wb!xfD;zis$l5aKCV^W>PS7edxhKnDLaGeaF6DwNp>Q{Ua!z79+QN7KU6AZd~`bCKR`a+ZmJ*~dFLQ{eS7VsEpz#}g%x2k{SU<%|SZZ>^+Jy>QC-d)8Lv z`fCFvn0RC2V_Oef`s}B-^D4T;EBAU7_7YK43Og(W)yIe0?uZjH@3tRhUre%yR@bJd zyFU*WaJTmy?-%&6l%D4Ufk3I%9LpM+!!i!09*ez0Vj3!;L3vq*EXA@N-E4!sDutl3 z@BT9p=VYwPbc^_cMst(iS4ufmWOOkpN9W1mx%eW4r!aSj`!hD1Pw^$)t&SI|tH)W? zbp%2R9>jWhguU-h);b(Fg}Hd&^0?T#g4n>viz&V4*E-r`K@S!TixT#JRc-n_aHWv43n9KVHGVEds6ntZ z4JX}naNpxNbB(Pqs6r7l{B?|igFcJN&deGBloITQI@FE%NmZ?mA@)t22pjl*2Wn?2 zUNmozj)VG2C@^c{BP+A0?1GCgo^WBAz$bd)Nh@e1Gfx;iRLgT8=-I=u2a(zx(|SAf zN^T*X~nV($BkV0+lqgtjKGKRX=~^oE+blpeSmk@@%D= zpdUEERq1s?A@IDrbyAwQnCG|-afFC^>ctq?zRhge%-`leu$B_91&nV==Uw=-V5nBI zh%q`x0=Lro28)7FtQ4NM)~;{&KGg*v`N>%EvlA21^^BVcCp^%;-wdi|x+lX$Sme$& zbXk}Qz_lbkC5Xl8A6L4BSCY5!peA!zcv_0$dn%W0&lwKZ^`TXUWAuZDR@(n8XOfTj zD(ZkL#?qwY=y{J4kgWWj#Bq{H+UOz>5mSwGSB}T`g1p8vrUPq=eR}v54vR!VG!fX{0`O^+#0p0ge^ixAh zkTRK8;81)Sa8f4hPb+y}&4-Q)Q=tYFDZ#V)3gQ7CCAwHP?AO-cyxR0xS=d((upOf( zd!n#>Ny;H65PqyBDJsePk--W3ka(+GL7!3VM4Uf2xd$)tPqYuEHg{gWPYWt%Z#YPF zH^^EY=2;96$(LUQq?q_}!lNDz{v6Q#B*iejfcLRlN&OgjNe=@(Z+}%q)LkbCLF%ie8p>zheb6

      (zd`Htx_G4rFnh6)=}3X@uMVbnaDf`!>n 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 r&C7ND2N{U80UrBTlgq5B!L+Ucc0UX0N6N> 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 literal 0 HcmV?d00001 diff --git a/system/media/guide/kohana/welcome.png b/system/media/guide/kohana/welcome.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7ac3d8427e3ef55cb8b0ce9862d238bef17be9 GIT binary patch literal 754 zcmVRq00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m?d01m?e$8V@)00007bV*G`2igk( z01gVyO9u@A00MGJL_t(o!|j(bZsRZzMc?EsvI@f|pikgUlWLDZl}@u=%I(rnWpad6 zZbF@4b%MY}sJr|vVkwGbTXqn@2#^mD(2$(@!*9*dGD+eGXWZe0ZV?G0igV}s6S_9a!#LTD)+dFS&H05%V zXy{WeZ8v+mUtvZ?g;{}_u{49Gq^S51^B7m#pQ@_Bte`L>ryTKGQFZ>q@*F{uLXyDA zfs}%LRQH@DITxf9)ZeS<4?ZVJxS$lABz1Oh?5yKx_bd9nwj_BUCB>s$DS80{k{qrRuqEz~6x>14eAI^|_i;?Yh14CS6(V}1UDW8` z;!JVv;~lQIXFZf_loXurL_OHkYUpl@@b=oV6B$Wl>wgE|-j~;L8Ri+lg68$*q(1Wd zI^(H!&^&WrLA=}Y_t8UBvMC!@f97Ap_4M4ZpoT0z?|<49wr>ia9CVRqZx?#C8Legc z;-nbs3~GNzb$<(+iq-0d+TT##-!k7mLw5ZoT#G#81)zC7xMv3BzvnILEt)DDRhC`eil8W zxpwp~5M1cy`+~Je4SUfLQIf}ZdxrK%?|t9Tk^lez07*qoM6N<$f(4^! array( + 'custom' => 'very nice email address you have there', + ), +); diff --git a/system/messages/validation.php b/system/messages/validation.php new file mode 100644 index 0000000..2d79cb4 --- /dev/null +++ b/system/messages/validation.php @@ -0,0 +1,27 @@ + ':field must contain only letters', + 'alpha_dash' => ':field must contain only numbers, letters and dashes', + 'alpha_numeric' => ':field must contain only letters and numbers', + 'color' => ':field must be a color', + 'credit_card' => ':field must be a credit card number', + 'date' => ':field must be a date', + 'decimal' => ':field must be a decimal with :param2 places', + 'digit' => ':field must be a digit', + 'email' => ':field must be an email address', + 'email_domain' => ':field must contain a valid email domain', + 'equals' => ':field must equal :param2', + 'exact_length' => ':field must be exactly :param2 characters long', + 'in_array' => ':field must be one of the available options', + 'ip' => ':field must be an ip address', + 'matches' => ':field must be the same as :param3', + 'min_length' => ':field must be at least :param2 characters long', + 'max_length' => ':field must not exceed :param2 characters long', + 'not_empty' => ':field must not be empty', + 'numeric' => ':field must be numeric', + 'phone' => ':field must be a phone number', + 'range' => ':field must be within the range of :param2 to :param3', + 'regex' => ':field does not match the required format', + 'url' => ':field must be a url', +); diff --git a/system/tests/kohana/ArrTest.php b/system/tests/kohana/ArrTest.php new file mode 100644 index 0000000..150016f --- /dev/null +++ b/system/tests/kohana/ArrTest.php @@ -0,0 +1,696 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_ArrTest extends Unittest_TestCase +{ + /** + * Provides test data for test_callback() + * + * @return array + */ + public function provider_callback() + { + return array( + // Tests.... + // That no parameters returns null + array('function', array('function', NULL)), + // That we can get an array of parameters values + array('function(1,2,3)', array('function', array('1', '2', '3'))), + // That it's not just using the callback "function" + array('different_name(harry,jerry)', array('different_name', array('harry', 'jerry'))), + // That static callbacks are parsed into arrays + array('kohana::appify(this)', array(array('kohana', 'appify'), array('this'))), + // Spaces are preserved in parameters + array('deal::make(me, my mate )', array(array('deal', 'make'), array('me', ' my mate '))) + // TODO: add more cases + ); + } + + /** + * Tests Arr::callback() + * + * @test + * @dataProvider provider_callback + * @param string $str String to parse + * @param array $expected Callback and its parameters + */ + public function test_callback($str, $expected) + { + $result = Arr::callback($str); + + $this->assertSame(2, count($result)); + $this->assertSame($expected, $result); + } + + /** + * Provides test data for test_extract + * + * @return array + */ + public function provider_extract() + { + return array( + array( + array('kohana' => 'awesome', 'blueflame' => 'was'), + array('kohana', 'cakephp', 'symfony'), + NULL, + array('kohana' => 'awesome', 'cakephp' => NULL, 'symfony' => NULL) + ), + // I realise noone should EVER code like this in real life, + // but unit testing is very very very very boring + array( + array('chocolate cake' => 'in stock', 'carrot cake' => 'in stock'), + array('carrot cake', 'humble pie'), + 'not in stock', + array('carrot cake' => 'in stock', 'humble pie' => 'not in stock'), + ), + array( + // Source Array + array('level1' => array('level2a' => 'value 1', 'level2b' => 'value 2')), + // Paths to extract + array('level1.level2a', 'level1.level2b'), + // Default + NULL, + // Expected Result + array('level1' => array('level2a' => 'value 1', 'level2b' => 'value 2')), + ), + array( + // Source Array + array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2')), + // Paths to extract + array('level1a', 'level1b.level2b'), + // Default + NULL, + // Expected Result + array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2')), + ), + array( + // Source Array + array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2')), + // Paths to extract + array('level1a', 'level1b.level2b', 'level1c', 'level1d.notfound'), + // Default + 'default', + // Expected Result + array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2'), 'level1c' => 'default', 'level1d' => array('notfound' => 'default')), + ), + ); + } + + /** + * Tests Arr::extract() + * + * @test + * @dataProvider provider_extract + * @param array $array + * @param array $paths + * @param mixed $default + * @param array $expected + */ + public function test_extract(array $array, array $paths, $default, $expected) + { + $array = Arr::extract($array, $paths, $default); + + $this->assertSame(count($expected), count($array)); + $this->assertSame($expected, $array); + } + + /** + * Provides test data for test_pluck + * + * @return array + */ + public function provider_pluck() + { + return array( + array( + array( + array('id' => 20, 'name' => 'John Smith'), + array('name' => 'Linda'), + array('id' => 25, 'name' => 'Fred'), + ), + 'id', + array(20, 25) + ), + ); + } + + /** + * Tests Arr::pluck() + * + * @test + * @dataProvider provider_pluck + * @param array $array + * @param string $key + * @param array $expected + */ + public function test_pluck(array $array, $key, $expected) + { + $array = Arr::pluck($array, $key); + + $this->assertSame(count($expected), count($array)); + $this->assertSame($expected, $array); + } + + /** + * Provides test data for test_get() + * + * @return array + */ + public function provider_get() + { + return array( + array(array('uno', 'dos', 'tress'), 1, NULL, 'dos'), + array(array('we' => 'can', 'make' => 'change'), 'we', NULL, 'can'), + + array(array('uno', 'dos', 'tress'), 10, NULL, NULL), + array(array('we' => 'can', 'make' => 'change'), 'he', NULL, NULL), + array(array('we' => 'can', 'make' => 'change'), 'he', 'who', 'who'), + array(array('we' => 'can', 'make' => 'change'), 'he', array('arrays'), array('arrays')), + ); + } + + /** + * Tests Arr::get() + * + * @test + * @dataProvider provider_get() + * @param array $array Array to look in + * @param string|integer $key Key to look for + * @param mixed $default What to return if $key isn't set + * @param mixed $expected The expected value returned + */ + public function test_get(array $array, $key, $default, $expected) + { + $this->assertSame( + $expected, + Arr::get($array, $key, $default) + ); + } + + /** + * Provides test data for test_is_assoc() + * + * @return array + */ + public function provider_is_assoc() + { + return array( + array(array('one', 'two', 'three'), FALSE), + array(array('one' => 'o clock', 'two' => 'o clock', 'three' => 'o clock'), TRUE), + ); + } + + /** + * Tests Arr::is_assoc() + * + * @test + * @dataProvider provider_is_assoc + * @param array $array Array to check + * @param boolean $expected Is $array assoc + */ + public function test_is_assoc(array $array, $expected) + { + $this->assertSame( + $expected, + Arr::is_assoc($array) + ); + } + + /** + * Provides test data for test_is_array() + * + * @return array + */ + public function provider_is_array() + { + return array( + array($a = array('one', 'two', 'three'), TRUE), + array(new ArrayObject($a), TRUE), + array(new ArrayIterator($a), TRUE), + array('not an array', FALSE), + array(new stdClass, FALSE), + ); + } + + /** + * Tests Arr::is_array() + * + * @test + * @dataProvider provider_is_array + * @param mixed $value Value to check + * @param boolean $expected Is $value an array? + */ + public function test_is_array($array, $expected) + { + $this->assertSame( + $expected, + Arr::is_array($array) + ); + } + + public function provider_merge() + { + return array( + // Test how it merges arrays and sub arrays with assoc keys + array( + array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane')), + array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane')), + array('name' => 'mary', 'children' => array('jane')), + ), + // See how it merges sub-arrays with numerical indexes + array( + array(array('test1'), array('test2'), array('test3')), + array(array('test1'), array('test2')), + array(array('test2'), array('test3')), + ), + array( + array(array(array('test1')), array(array('test2')), array(array('test3'))), + array(array(array('test1')), array(array('test2'))), + array(array(array('test2')), array(array('test3'))), + ), + array( + array('a' => array('test1','test2'), 'b' => array('test2','test3')), + array('a' => array('test1'), 'b' => array('test2')), + array('a' => array('test2'), 'b' => array('test3')), + ), + array( + array('digits' => array(0, 1, 2, 3)), + array('digits' => array(0, 1)), + array('digits' => array(2, 3)), + ), + // See how it manages merging items with numerical indexes + array( + array(0, 1, 2, 3), + array(0, 1), + array(2, 3), + ), + // Try and get it to merge assoc. arrays recursively + array( + array('foo' => 'bar', array('temp' => 'life')), + array('foo' => 'bin', array('temp' => 'name')), + array('foo' => 'bar', array('temp' => 'life')), + ), + // Bug #3139 + array( + array('foo' => array('bar')), + array('foo' => 'bar'), + array('foo' => array('bar')), + ), + array( + array('foo' => 'bar'), + array('foo' => array('bar')), + array('foo' => 'bar'), + ), + + // data set #9 + // Associative, Associative + array( + array('a' => 'K', 'b' => 'K', 'c' => 'L'), + array('a' => 'J', 'b' => 'K'), + array('a' => 'K', 'c' => 'L'), + ), + // Associative, Indexed + array( + array('a' => 'J', 'b' => 'K', 'L'), + array('a' => 'J', 'b' => 'K'), + array('K', 'L'), + ), + // Associative, Mixed + array( + array('a' => 'J', 'b' => 'K', 'K', 'c' => 'L'), + array('a' => 'J', 'b' => 'K'), + array('K', 'c' => 'L'), + ), + + // data set #12 + // Indexed, Associative + array( + array('J', 'K', 'a' => 'K', 'c' => 'L'), + array('J', 'K'), + array('a' => 'K', 'c' => 'L'), + ), + // Indexed, Indexed + array( + array('J', 'K', 'L'), + array('J', 'K'), + array('K', 'L'), + ), + // Indexed, Mixed + array( + array('K', 'K', 'c' => 'L'), + array('J', 'K'), + array('K', 'c' => 'L'), + ), + + // data set #15 + // Mixed, Associative + array( + array('a' => 'K', 'K', 'c' => 'L'), + array('a' => 'J', 'K'), + array('a' => 'K', 'c' => 'L'), + ), + // Mixed, Indexed + array( + array('a' => 'J', 'K', 'L'), + array('a' => 'J', 'K'), + array('J', 'L'), + ), + // Mixed, Mixed + array( + array('a' => 'K', 'L'), + array('a' => 'J', 'K'), + array('a' => 'K', 'L'), + ), + + // Bug #3141 + array( + array('servers' => array(array('1.1.1.1', 4730), array('2.2.2.2', 4730))), + array('servers' => array(array('1.1.1.1', 4730))), + array('servers' => array(array('2.2.2.2', 4730))), + ), + ); + } + + /** + * + * @test + * @dataProvider provider_merge + */ + public function test_merge($expected, $array1, $array2) + { + $this->assertSame( + $expected, + Arr::merge($array1,$array2) + ); + } + + /** + * Provides test data for test_path() + * + * @return array + */ + public function provider_path() + { + $array = array( + 'foobar' => array('definition' => 'lost'), + 'kohana' => 'awesome', + 'users' => array( + 1 => array('name' => 'matt'), + 2 => array('name' => 'john', 'interests' => array('hocky' => array('length' => 2), 'football' => array())), + 3 => 'frank', // Issue #3194 + ), + 'object' => new ArrayObject(array('iterator' => TRUE)), // Iterable object should work exactly the same + ); + + return array( + // Tests returns normal values + array($array['foobar'], $array, 'foobar'), + array($array['kohana'], $array, 'kohana'), + array($array['foobar']['definition'], $array, 'foobar.definition'), + // Custom delimiters + array($array['foobar']['definition'], $array, 'foobar/definition', NULL, '/'), + // We should be able to use NULL as a default, returned if the key DNX + array(NULL, $array, 'foobar.alternatives', NULL), + array(NULL, $array, 'kohana.alternatives', NULL), + // Try using a string as a default + array('nothing', $array, 'kohana.alternatives', 'nothing'), + // Make sure you can use arrays as defaults + array(array('far', 'wide'), $array, 'cheese.origins', array('far', 'wide')), + // Ensures path() casts ints to actual integers for keys + array($array['users'][1]['name'], $array, 'users.1.name'), + // Test that a wildcard returns the entire array at that "level" + array($array['users'], $array, 'users.*'), + // Now we check that keys after a wilcard will be processed + array(array(0 => array(0 => 2)), $array, 'users.*.interests.*.length'), + // See what happens when it can't dig any deeper from a wildcard + array(NULL, $array, 'users.*.fans'), + // Starting wildcards, issue #3269 + array(array('matt', 'john'), $array['users'], '*.name'), + // Path as array, issue #3260 + array($array['users'][2]['name'], $array, array('users', 2, 'name')), + array($array['object']['iterator'], $array, 'object.iterator'), + ); + } + + /** + * Tests Arr::path() + * + * @test + * @dataProvider provider_path + * @param string $path The path to follow + * @param mixed $default The value to return if dnx + * @param boolean $expected The expected value + * @param string $delimiter The path delimiter + */ + public function test_path($expected, $array, $path, $default = NULL, $delimiter = NULL) + { + $this->assertSame( + $expected, + Arr::path($array, $path, $default, $delimiter) + ); + } + + /** + * Provides test data for test_path() + * + * @return array + */ + public function provider_set_path() + { + return array( + // Tests returns normal values + array(array('foo' => 'bar'), array(), 'foo', 'bar'), + array(array('kohana' => array('is' => 'awesome')), array(), 'kohana.is', 'awesome'), + array(array('kohana' => array('is' => 'cool', 'and' => 'slow')), + array('kohana' => array('is' => 'cool')), 'kohana.and', 'slow'), + // Custom delimiters + array(array('kohana' => array('is' => 'awesome')), array(), 'kohana/is', 'awesome', '/'), + // Ensures set_path() casts ints to actual integers for keys + array(array('foo' => array('bar')), array('foo' => array('test')), 'foo.0', 'bar'), + ); + } + + /** + * Tests Arr::path() + * + * @test + * @dataProvider provider_set_path + * @param string $path The path to follow + * @param boolean $expected The expected value + * @param string $delimiter The path delimiter + */ + public function test_set_path($expected, $array, $path, $value, $delimiter = NULL) + { + Arr::set_path($array, $path, $value, $delimiter); + + $this->assertSame($expected, $array); + } + + /** + * Provides test data for test_range() + * + * @return array + */ + public function provider_range() + { + return array( + array(1, 2), + array(1, 100), + array(25, 10), + ); + } + + /** + * Tests Arr::range() + * + * @dataProvider provider_range + * @param integer $step The step between each value in the array + * @param integer $max The max value of the range (inclusive) + */ + public function test_range($step, $max) + { + $range = Arr::range($step, $max); + + $this->assertSame( (int) floor($max / $step), count($range)); + + $current = $step; + + foreach ($range as $key => $value) + { + $this->assertSame($key, $value); + $this->assertSame($current, $key); + $this->assertLessThanOrEqual($max, $key); + $current += $step; + } + } + + /** + * Provides test data for test_unshift() + * + * @return array + */ + public function provider_unshift() + { + return array( + array(array('one' => '1', 'two' => '2',), 'zero', '0'), + array(array('step 1', 'step 2', 'step 3'), 'step 0', 'wow') + ); + } + + /** + * Tests Arr::unshift() + * + * @test + * @dataProvider provider_unshift + * @param array $array + * @param string $key + * @param mixed $value + */ + public function test_unshift(array $array, $key, $value) + { + $original = $array; + + Arr::unshift($array, $key, $value); + + $this->assertNotSame($original, $array); + $this->assertSame(count($original) + 1, count($array)); + $this->assertArrayHasKey($key, $array); + + $this->assertSame($value, reset($array)); + $this->assertSame(key($array), $key); + } + + /** + * Provies test data for test_overwrite + * + * @return array Test Data + */ + public function provider_overwrite() + { + return array( + array( + array('name' => 'Henry', 'mood' => 'tired', 'food' => 'waffles', 'sport' => 'checkers'), + array('name' => 'John', 'mood' => 'bored', 'food' => 'bacon', 'sport' => 'checkers'), + array('name' => 'Matt', 'mood' => 'tired', 'food' => 'waffles'), + array('name' => 'Henry', 'age' => 18,), + ), + ); + } + + /** + * + * @test + * @dataProvider provider_overwrite + */ + public function test_overwrite($expected, $arr1, $arr2, $arr3 = array(), $arr4 = array()) + { + $this->assertSame( + $expected, + Arr::overwrite($arr1, $arr2, $arr3, $arr4) + ); + } + + /** + * Provides test data for test_map + * + * @return array Test Data + */ + public function provider_map() + { + return array( + array('strip_tags', array('

      foobar

      '), NULL, array('foobar')), + array('strip_tags', array(array('

      foobar

      '), array('

      foobar

      ')), NULL, array(array('foobar'), array('foobar'))), + array( + 'strip_tags', + array( + 'foo' => '

      foobar

      ', + 'bar' => '

      foobar

      ', + ), + NULL, + array( + 'foo' => 'foobar', + 'bar' => 'foobar', + ), + ), + array( + 'strip_tags', + array( + 'foo' => '

      foobar

      ', + 'bar' => '

      foobar

      ', + ), + array('foo'), + array( + 'foo' => 'foobar', + 'bar' => '

      foobar

      ', + ), + ), + array( + array( + 'strip_tags', + 'trim', + ), + array( + 'foo' => '

      foobar

      ', + 'bar' => '

      foobar

      ', + ), + NULL, + array( + 'foo' => 'foobar', + 'bar' => 'foobar', + ), + ), + ); + } + + /** + * + * @test + * @dataProvider provider_map + */ + public function test_map($method, $source, $keys, $expected) + { + $this->assertSame( + $expected, + Arr::map($method, $source, $keys) + ); + } + + /** + * Provides test data for test_flatten + * + * @return array Test Data + */ + public function provider_flatten() + { + return array( + array(array('set' => array('one' => 'something'), 'two' => 'other'), array('one' => 'something', 'two' => 'other')), + ); + } + + /** + * + * @test + * @dataProvider provider_flatten + */ + public function test_flatten($source, $expected) + { + $this->assertSame( + $expected, + Arr::flatten($source) + ); + } +} diff --git a/system/tests/kohana/Config/File/ReaderTest.php b/system/tests/kohana/Config/File/ReaderTest.php new file mode 100644 index 0000000..ab22d14 --- /dev/null +++ b/system/tests/kohana/Config/File/ReaderTest.php @@ -0,0 +1,94 @@ + + * @author Matt Button + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_Config_File_ReaderTest extends Kohana_Unittest_TestCase { + + /** + * If we don't pass a directory to the reader then it should assume + * that we want to search the dir 'config' by default + * + * @test + * @covers Kohana_Config_File_Reader + */ + public function test_default_search_dir_is_config() + { + $reader = new Kohana_Config_File_Reader; + + $this->assertAttributeSame('config', '_directory', $reader); + } + + /** + * If we pass a directory to the constructor of the file reader it + * should change the search directory + * + * @test + * @covers Kohana_Config_File_Reader + */ + public function test_constructor_sets_search_dir_from_param() + { + $reader = new Kohana_Config_File_Reader('gafloog'); + + $this->assertAttributeSame('gafloog', '_directory', $reader); + } + + /** + * If the config dir does not exist then the function should just + * return an empty array + * + * @test + * @covers Kohana_Config_File_Reader::load + */ + public function test_load_returns_empty_array_if_conf_dir_dnx() + { + $config = new Kohana_Config_File_Reader('gafloogle'); + + $this->assertSame(array(), $config->load('values')); + } + + /** + * If the requested config group does not exist then the reader + * should return an empty array + * + * @test + * @covers Kohana_Config_File_Reader::load + */ + public function test_load_returns_empty_array_if_conf_dnx() + { + $config = new Kohana_Config_File_Reader; + + $this->assertSame(array(), $config->load('gafloogle')); + } + + /** + * Test that the load() function is actually loading the + * configuration from the files. + * + * @test + * @covers Kohana_Config_File_Reader::load + */ + public function test_loads_config_from_files() + { + $config = new Kohana_Config_File_Reader; + + $values = $config->load('inflector'); + + // Due to the way the cascading filesystem works there could be + // any number of modifications to the system config in the + // actual output. Therefore to increase compatability we just + // check that we've got an array and that it's not empty + $this->assertNotSame(array(), $values); + $this->assertInternalType('array', $values); + } +} diff --git a/system/tests/kohana/Config/GroupTest.php b/system/tests/kohana/Config/GroupTest.php new file mode 100644 index 0000000..66fd058 --- /dev/null +++ b/system/tests/kohana/Config/GroupTest.php @@ -0,0 +1,192 @@ + + * @author Matt Button + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_Config_GroupTest extends Kohana_Unittest_TestCase +{ + /** + * Create a mock Kohana_Config instance + * + * @return Kohana_Config + */ + public function get_mock_config() + { + return new Kohana_Config; + } + + /** + * Gets a fresh instance of Kohana_Config_Group + * + * @param string $group Config Group name + * @param array $config Configuration + * @param Kohana_Config $instance Instance of Kohana_Config + * @return Kohana_Config_Group + */ + public function get_mock_group($group, $config = array(), $instance = NULL) + { + if ($instance === NULL) + { + $instance = $this->get_mock_config(); + } + + return new Kohana_Config_Group($instance, $group, $config); + } + + /** + * The group name and group's config values should be loaded into the object + * by the constructor + * + * @test + * @covers Kohana_Config_Group + */ + public function test_loads_group_name_and_values_in_constructor() + { + $group_name = 'information'; + $group_values = array('var' => 'value'); + + $group = $this->get_mock_group($group_name, $group_values); + + // Now usually we'd just use assertAttributeSame, but that tries to get at protected properties + // by casting the object in question into an array. This usually works fine, but as Kohana_Config_Group + // is a subclass of ArrayObject, casting to an array returns the config items! + // Therefore we have to use this little workaround + $this->assertSame($group_name, $group->group_name()); + $this->assertSame($group_values, $group->getArrayCopy()); + } + + /** + * A config group may not exist (or may not have any values) when it is loaded. + * The config group should allow for this situation and not complain + * + * @test + * @covers Kohana_Config_Group + */ + public function test_allows_empty_group_values() + { + $group = $this->get_mock_group('informatica'); + + $this->assertSame(array(), $group->getArrayCopy()); + } + + /** + * When get() is called it should fetch the config value specified + * + * @test + * @covers Kohana_Config_Group::get + */ + public function test_get_fetches_config_value() + { + $group = $this->get_mock_group('kohana', array('status' => 'awesome')); + + $this->assertSame('awesome', $group->get('status')); + } + + /** + * If a config option does not exist then get() should return the default value, which is + * NULL by default + * + * @test + * @covers Kohana_Config_Group::get + */ + public function test_get_returns_default_value_if_config_option_dnx() + { + $group = $this->get_mock_group('kohana'); + + $this->assertSame(NULL, $group->get('problems', NULL)); + $this->assertSame('nada', $group->get('problems', 'nada')); + } + + /** + * We should be able to modify existing configuration items using set() + * + * @test + * @covers Kohana_Config_Group::set + */ + public function test_set_modifies_existing_config() + { + $group = $this->get_mock_group('kohana', array('status' => 'pre-awesome')); + + $group->set('status', 'awesome'); + + $this->assertSame('awesome', $group->get('status')); + } + + /** + * If we modify the config via set() [$var] or ->$var then the change should be passed to + * the parent config instance so that the config writers can be notified. + * + * The modification to the config should also stick + * + * @test + * @covers Kohana_Config_Group::offsetSet + */ + public function test_writes_changes_to_config() + { + $mock = $this->getMock('Kohana_Config', array('_write_config')); + + $mock + ->expects($this->exactly(3)) + ->method('_write_config') + ->with('kohana', 'status', $this->LogicalOr('totally', 'maybe', 'not')); + + $group = $this->get_mock_group('kohana', array('status' => 'kool'), $mock); + + $group['status'] = 'totally'; + + $group->status = 'maybe'; + + $group->set('status', 'not'); + } + + /** + * Calling as_array() should return the full array, inc. any modifications + * + * @test + * @covers Kohana_Config_Group::as_array + */ + public function test_as_array_returns_full_array() + { + $config = $this->get_mock_group('something', array('var' => 'value')); + + $this->assertSame(array('var' => 'value'), $config->as_array()); + + // Now change some vars **ahem** + $config->var = 'LOLCAT'; + $config->lolcat = 'IN UR CODE'; + + $this->assertSame( + array('var' => 'LOLCAT', 'lolcat' => 'IN UR CODE'), + $config->as_array() + ); + + // And if we remove an item it should be removed from the exported array + unset($config['lolcat']); + $this->assertSame(array('var' => 'LOLCAT'), $config->as_array()); + } + + /** + * Casting the object to a string should serialize the output of as_array + * + * @test + * @covers Kohana_Config_Group::__toString + */ + public function test_to_string_serializes_array_output() + { + $vars = array('kohana' => 'cool', 'unit_tests' => 'boring'); + $config = $this->get_mock_group('hehehe', $vars); + + $this->assertSame(serialize($vars), (string) $config); + } +} + diff --git a/system/tests/kohana/ConfigTest.php b/system/tests/kohana/ConfigTest.php new file mode 100644 index 0000000..5766d6e --- /dev/null +++ b/system/tests/kohana/ConfigTest.php @@ -0,0 +1,406 @@ + + * @author Matt Button + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_ConfigTest extends Unittest_TestCase +{ + + /** + * When a config object is initially created there should be + * no readers attached + * + * @test + * @covers Config + */ + public function test_initially_there_are_no_sources() + { + $config = new Config; + + $this->assertAttributeSame(array(), '_sources', $config); + } + + /** + * Test that calling attach() on a kohana config object + * adds the specified reader to the config object + * + * @test + * @covers Config::attach + */ + public function test_attach_adds_reader_and_returns_this() + { + $config = new Config; + $reader = $this->getMock('Kohana_Config_Reader'); + + $this->assertSame($config, $config->attach($reader)); + + $this->assertAttributeContains($reader, '_sources', $config); + } + + /** + * By default (or by passing TRUE as the second parameter) the config object + * should prepend the reader to the front of the readers queue + * + * @test + * @covers Config::attach + */ + public function test_attach_adds_reader_to_front_of_queue() + { + $config = new Config; + + $reader1 = $this->getMock('Kohana_Config_Reader'); + $reader2 = $this->getMock('Kohana_Config_Reader'); + + $config->attach($reader1); + $config->attach($reader2); + + // Rather than do two assertContains we'll do an assertSame to assert + // the order of the readers + $this->assertAttributeSame(array($reader2, $reader1), '_sources', $config); + + // Now we test using the second parameter + $config = new Config; + + $config->attach($reader1); + $config->attach($reader2, TRUE); + + $this->assertAttributeSame(array($reader2, $reader1), '_sources', $config); + } + + /** + * Test that attaching a new reader (and passing FALSE as second param) causes + * phpunit to append the reader rather than prepend + * + * @test + * @covers Config::attach + */ + public function test_attach_can_add_reader_to_end_of_queue() + { + $config = new Config; + $reader1 = $this->getMock('Kohana_Config_Reader'); + $reader2 = $this->getMock('Kohana_Config_Reader'); + + $config->attach($reader1); + $config->attach($reader2, FALSE); + + $this->assertAttributeSame(array($reader1, $reader2), '_sources', $config); + } + + /** + * Calling detach() on a config object should remove it from the queue of readers + * + * @test + * @covers Config::detach + */ + public function test_detach_removes_reader_and_returns_this() + { + $config = new Config; + + // Due to the way phpunit mock generator works if you try and mock a class + // that has already been used then it just re-uses the first's name + + // To get around this we have to specify a totally random name for the second mock object + $reader1 = $this->getMock('Kohana_Config_Reader'); + $reader2 = $this->getMock('Kohana_Config_Reader', array(), array(), 'MY_AWESOME_READER'); + + $config->attach($reader1); + $config->attach($reader2); + + $this->assertSame($config, $config->detach($reader1)); + + $this->assertAttributeNotContains($reader1, '_sources', $config); + $this->assertAttributeContains($reader2, '_sources', $config); + + $this->assertSame($config, $config->detach($reader2)); + + $this->assertAttributeNotContains($reader2, '_sources', $config); + } + + /** + * detach() should return $this even if the specified reader does not exist + * + * @test + * @covers Config::detach + */ + public function test_detach_returns_this_even_when_reader_dnx() + { + $config = new Config; + $reader = $this->getMock('Kohana_Config_Reader'); + + $this->assertSame($config, $config->detach($reader)); + } + + /** + * If we request a config variable with a dot path then + * Config::load() should load the group and return the requested variable + * + * @test + * @covers Config::load + */ + public function test_load_can_get_var_from_dot_path() + { + $config = new Config; + + $reader = $this->getMock('Kohana_Config_Reader', array('load')); + + $reader + ->expects($this->once()) + ->method('load') + ->with('beer') + ->will($this->returnValue(array('stout' => 'Guinness'))); + + $config->attach($reader); + + $this->assertSame('Guinness', $config->load('beer.stout')); + } + + /** + * If we've already loaded a config group then the correct variable + * should be returned if we use the dot path notation to to request + * a var + * + * @test + * @covers Config::load + */ + public function test_load_can_get_var_from_dot_path_for_loaded_group() + { + $config = new Config; + + $reader = $this->getMock('Kohana_Config_Reader', array('load')); + + $reader + ->expects($this->once()) + ->method('load') + ->with('beer') + ->will($this->returnValue(array('stout' => 'Guinness'))); + + $config->attach($reader); + + $config->load('beer'); + + $this->assertSame('Guinness', $config->load('beer.stout')); + } + + /** + * If load() is called and there are no readers present then it should throw + * a kohana exception + * + * @test + * @covers Config::load + * @expectedException Kohana_Exception + */ + public function test_load_throws_exception_if_there_are_no_sources() + { + // The following code should throw an exception and phpunit will catch / handle it + // (see the @expectedException doccomment) + $config = new Kohana_config; + + $config->load('random'); + } + + /** + * Provides test data for test_load_throws_exception_if_no_group_is_given() + * + * @return array + */ + public function provider_load_throws_exception_if_no_group_is_given() + { + return array( + array(NULL), + array(''), + array(array()), + array(array('foo' => 'bar')), + array(new StdClass), + ); + } + + /** + * If an invalid group name is specified then an exception should be thrown. + * + * Invalid means it's either a non-string value, or empty + * + * @test + * @dataProvider provider_load_throws_exception_if_no_group_is_given + * @covers Config::load + * @expectedException Kohana_Exception + */ + public function test_load_throws_exception_if_invalid_group($value) + { + $config = new Kohana_Config; + + $reader = $this->getMock('Kohana_Config_Reader'); + + $config->attach($reader); + + $config->load($value); + } + + /** + * Make sure that _write_config() passes the changed configuration to all + * writers in the queue + * + * @test + * @covers Kohana_Config + */ + public function test_write_config_passes_changed_config_to_all_writers() + { + $config = new Kohana_Config; + + $reader1 = $this->getMock('Kohana_Config_Reader'); + $writer1 = $this->getMock('Kohana_Config_Writer', array('write')); + $writer2 = $this->getMock('Kohana_Config_Writer', array('write')); + + $writer1 + ->expects($this->once()) + ->method('write') + ->with('some_group', 'key', 'value'); + + $writer2 + ->expects($this->once()) + ->method('write') + ->with('some_group', 'key', 'value'); + + $config->attach($reader1)->attach($writer1)->attach($writer2); + + $config->_write_config('some_group', 'key', 'value'); + } + + /** + * Config sources are stored in a stack, make sure that config at the bottom + * of the stack is overriden by config at the top + * + * @test + * @covers Config::load + */ + public function test_config_is_loaded_from_top_to_bottom_of_stack() + { + $group_name = 'lolumns'; + + $reader1 = $this->getMock('Kohana_Config_Reader', array('load'), array(), 'Unittest_Config_Reader_1'); + $reader2 = $this->getMock('Kohana_Config_Reader', array('load'), array(), 'Unittest_Config_Reader_2'); + + $reader1 + ->expects($this->once()) + ->method('load') + ->with($group_name) + ->will($this->returnValue(array('foo' => 'bar', 'kohana' => 'awesome', 'life' => array('normal', 'fated')))); + + $reader2 + ->expects($this->once()) + ->method('load') + ->with($group_name) + ->will($this->returnValue(array('kohana' => 'sweet', 'music' => 'tasteful', 'life' => array('extraordinary', 'destined')))); + + $config = new Kohana_Config; + + // Attach $reader1 at the "top" and reader2 at the "bottom" + $config->attach($reader1)->attach($reader2, FALSE); + + $this->assertSame( + array( + 'kohana' => 'awesome', + 'music' => 'tasteful', + 'life' => array( + 'extraordinary', + 'destined', + 'normal', + 'fated', + ), + 'foo' => 'bar', + ), + $config->load($group_name)->as_array() + ); + } + + /** + * load() should keep a record of what config groups have been requested and if + * a group is requested more than once the first instance should be returned + * + * @test + * @covers Config::load + */ + public function test_load_reuses_config_groups() + { + $reader = $this->getMock('Kohana_Config_Reader', array('load')); + $reader + ->expects($this->once()) + ->method('load') + ->with('something') + ->will($this->returnValue(array())); + + $config = new Kohana_Config; + + $config->attach($reader); + + $group = $config->load('something'); + + $this->assertSame($group, $config->load('something')); + } + + /** + * When we call copy() we expect it to copy the merged config to all writers + * + * @TODO This test sucks due to limitations in the phpunit mock generator. MAKE THIS AWESOME AGAIN! + * @test + * @covers Kohana_Config::copy + */ + public function test_copy_copies_merged_config_to_all_writers() + { + $config = new Kohana_Config; + + $reader1 = $this->getMock('Kohana_Config_Reader', array('load')); + $reader2 = $this->getMock('Kohana_Config_Reader', array('load')); + + $reader1 + ->expects($this->once()) + ->method('load') + ->with('something') + ->will($this->returnValue(array('pie' => 'good', 'kohana' => 'awesome'))); + + $reader2 + ->expects($this->once()) + ->method('load') + ->with('something') + ->will($this->returnValue(array('kohana' => 'good'))); + + $writer1 = $this->getMock('Kohana_Config_Writer', array('write')); + $writer2 = $this->getMock('Kohana_Config_Writer', array('write')); + + // Due to crazy limitations in phpunit's mocking engine we have to be fairly + // liberal here as to what order we receive the config items + // Good news is that order shouldn't matter *yay* + // + // Now save your eyes and skip the next... 13 lines! + $key = $this->logicalOr('pie', 'kohana'); + $val = $this->logicalOr('good', 'awesome'); + + $writer1 + ->expects($this->exactly(2)) + ->method('write') + ->with('something', clone $key, clone $val); + + $writer2 + ->expects($this->exactly(2)) + ->method('write') + ->with('something', clone $key, clone $val); + + $config + ->attach($reader1)->attach($reader2, FALSE) + ->attach($writer1)->attach($writer2); + + // Now let's get this thing going! + $config->copy('something'); + } +} diff --git a/system/tests/kohana/CookieTest.php b/system/tests/kohana/CookieTest.php new file mode 100644 index 0000000..9c1fe40 --- /dev/null +++ b/system/tests/kohana/CookieTest.php @@ -0,0 +1,177 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_CookieTest extends Unittest_TestCase +{ + + protected $_default_salt = 'AdaoidadnA£ASDNadnaoiwdnawd'; + /** + * Sets up the environment + */ + // @codingStandardsIgnoreStart + public function setUp() + // @codingStandardsIgnoreEnd + { + parent::setUp(); + + Cookie::$salt = $this->_default_salt; + } + + /** + * Tears down the environment + */ + // @codingStandardsIgnoreStart + public function tearDown() + // @codingStandardsIgnoreEnd + { + parent::tearDown(); + + Cookie::$salt = NULL; + } + + /** + * Provides test data for test_set() + * + * @return array + */ + public function provider_set() + { + return array( + array('foo', 'bar', NULL, TRUE), + array('foo', 'bar', 10, TRUE), + ); + } + + /** + * Tests cookie::set() + * + * @test + * @dataProvider provider_set + * @covers cookie::set + * @param mixed $key key to use + * @param mixed $value value to set + * @param mixed $exp exp to set + * @param boolean $expected Output for cookie::set() + */ + public function test_set($key, $value, $exp, $expected) + { + if (headers_sent()) { + $this->markTestSkipped('Cannot test setting cookies as headers have already been sent'); + } + + $this->assertSame($expected, cookie::set($key, $value, $exp)); + } + + /** + * Provides test data for test_get() + * + * @return array + */ + public function provider_get() + { + // setUp is called after the provider so we need to specify a + // salt here in order to use it in the provider + Cookie::$salt = $this->_default_salt; + + return array( + array('foo', Cookie::salt('foo', 'bar').'~bar', 'bar'), + array('bar', Cookie::salt('foo', 'bar').'~bar', NULL), + array(NULL, Cookie::salt('foo', 'bar').'~bar', NULL), + ); + } + + /** + * Tests cookie::set() + * + * @test + * @dataProvider provider_get + * @covers cookie::get + * @param mixed $key key to use + * @param mixed $value value to set + * @param boolean $expected Output for cookie::get() + */ + public function test_get($key, $value, $expected) + { + if (headers_sent()) { + $this->markTestSkipped('Cannot test setting cookies as headers have already been sent'); + } + + // Force $_COOKIE + if ($key !== NULL) + { + $_COOKIE[$key] = $value; + } + + $this->assertSame($expected, cookie::get($key)); + } + + /** + * Provides test data for test_delete() + * + * @return array + */ + public function provider_delete() + { + return array( + array('foo', TRUE), + ); + } + + /** + * Tests cookie::delete() + * + * @test + * @dataProvider provider_delete + * @covers cookie::delete + * @param mixed $key key to use + * @param boolean $expected Output for cookie::delete() + */ + public function test_delete($key, $expected) + { + if (headers_sent()) { + $this->markTestSkipped('Cannot test setting cookies as headers have already been sent'); + } + + $this->assertSame($expected, cookie::delete($key)); + } + + /** + * Provides test data for test_salt() + * + * @return array + */ + public function provider_salt() + { + return array( + array('foo', 'bar', 'b5773a6255d1deefc23f9f69bcc40fdc998e5802'), + ); + } + + /** + * Tests cookie::salt() + * + * @test + * @dataProvider provider_salt + * @covers cookie::salt + * @param mixed $key key to use + * @param mixed $value value to salt with + * @param boolean $expected Output for cookie::delete() + */ + public function test_salt($key, $value, $expected) + { + $this->assertSame($expected, cookie::salt($key, $value)); + } +} diff --git a/system/tests/kohana/CoreTest.php b/system/tests/kohana/CoreTest.php new file mode 100644 index 0000000..beb508b --- /dev/null +++ b/system/tests/kohana/CoreTest.php @@ -0,0 +1,369 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_CoreTest extends Unittest_TestCase +{ + + /** + * Provides test data for test_sanitize() + * + * @return array + */ + public function provider_sanitize() + { + return array( + // $value, $result + array('foo', 'foo'), + array("foo\r\nbar", "foo\nbar"), + array("foo\rbar", "foo\nbar"), + array("Is your name O\'reilly?", "Is your name O'reilly?") + ); + } + + /** + * Tests Kohana::santize() + * + * @test + * @dataProvider provider_sanitize + * @covers Kohana::sanitize + * @param boolean $value Input for Kohana::sanitize + * @param boolean $result Output for Kohana::sanitize + */ + public function test_sanitize($value, $result) + { + $this->setEnvironment(array('Kohana::$magic_quotes' => TRUE)); + + $this->assertSame($result, Kohana::sanitize($value)); + } + + /** + * Passing FALSE for the file extension should prevent appending any extension. + * See issue #3214 + * + * @test + * @covers Kohana::find_file + */ + public function test_find_file_no_extension() + { + // EXT is manually appened to the _file name_, not passed as the extension + $path = Kohana::find_file('classes', $file = 'Kohana/Core'.EXT, FALSE); + + $this->assertInternalType('string', $path); + + $this->assertStringEndsWith($file, $path); + } + + /** + * If a file can't be found then find_file() should return FALSE if + * only a single file was requested, or an empty array if multiple files + * (i.e. configuration files) were requested + * + * @test + * @covers Kohana::find_file + */ + public function test_find_file_returns_false_or_array_on_failure() + { + $this->assertFalse(Kohana::find_file('configy', 'zebra')); + + $this->assertSame(array(), Kohana::find_file('configy', 'zebra', NULL, TRUE)); + } + + /** + * Kohana::list_files() should return an array on success and an empty array on failure + * + * @test + * @covers Kohana::list_files + */ + public function test_list_files_returns_array_on_success_and_failure() + { + $files = Kohana::list_files('config'); + + $this->assertInternalType('array', $files); + $this->assertGreaterThan(3, count($files)); + + $this->assertSame(array(), Kohana::list_files('geshmuck')); + } + + /** + * Tests Kohana::globals() + * + * @test + * @covers Kohana::globals + */ + public function test_globals_removes_user_def_globals() + { + $GLOBALS = array('hackers' => 'foobar','name' => array('','',''), '_POST' => array()); + + Kohana::globals(); + + $this->assertEquals(array('_POST' => array()), $GLOBALS); + } + + /** + * Provides test data for testCache() + * + * @return array + */ + public function provider_cache() + { + return array( + // $value, $result + array('foo', 'hello, world', 10), + array('bar', NULL, 10), + array('bar', NULL, -10), + ); + } + + /** + * Tests Kohana::cache() + * + * @test + * @dataProvider provider_cache + * @covers Kohana::cache + * @param boolean $key Key to cache/get for Kohana::cache + * @param boolean $value Output from Kohana::cache + * @param boolean $lifetime Lifetime for Kohana::cache + */ + public function test_cache($key, $value, $lifetime) + { + Kohana::cache($key, $value, $lifetime); + $this->assertEquals($value, Kohana::cache($key)); + } + + /** + * Provides test data for test_message() + * + * @return array + */ + public function provider_message() + { + return array( + // $value, $result + array(':field must not be empty', 'validation', 'not_empty'), + array( + array( + 'alpha' => ':field must contain only letters', + 'alpha_dash' => ':field must contain only numbers, letters and dashes', + 'alpha_numeric' => ':field must contain only letters and numbers', + 'color' => ':field must be a color', + 'credit_card' => ':field must be a credit card number', + 'date' => ':field must be a date', + 'decimal' => ':field must be a decimal with :param2 places', + 'digit' => ':field must be a digit', + 'email' => ':field must be a email address', + 'email_domain' => ':field must contain a valid email domain', + 'equals' => ':field must equal :param2', + 'exact_length' => ':field must be exactly :param2 characters long', + 'in_array' => ':field must be one of the available options', + 'ip' => ':field must be an ip address', + 'matches' => ':field must be the same as :param2', + 'min_length' => ':field must be at least :param2 characters long', + 'max_length' => ':field must not exceed :param2 characters long', + 'not_empty' => ':field must not be empty', + 'numeric' => ':field must be numeric', + 'phone' => ':field must be a phone number', + 'range' => ':field must be within the range of :param2 to :param3', + 'regex' => ':field does not match the required format', + 'url' => ':field must be a url', + ), + 'validation', NULL, + ), + ); + } + + /** + * Tests Kohana::message() + * + * @test + * @dataProvider provider_message + * @covers Kohana::message + * @param boolean $expected Output for Kohana::message + * @param boolean $file File to look in for Kohana::message + * @param boolean $key Key for Kohana::message + */ + public function test_message($expected, $file, $key) + { + $this->markTestSkipped('This test is incredibly fragile and needs to be re-done'); + $this->assertEquals($expected, Kohana::message($file, $key)); + } + + /** + * Provides test data for test_error_handler() + * + * @return array + */ + public function provider_error_handler() + { + return array( + array(1, 'Foobar', 'foobar.php', __LINE__), + ); + } + + /** + * Tests Kohana::error_handler() + * + * @test + * @dataProvider provider_error_handler + * @covers Kohana::error_handler + * @param boolean $code Input for Kohana::sanitize + * @param boolean $error Input for Kohana::sanitize + * @param boolean $file Input for Kohana::sanitize + * @param boolean $line Output for Kohana::sanitize + */ + public function test_error_handler($code, $error, $file, $line) + { + $error_level = error_reporting(); + error_reporting(E_ALL); + try + { + Kohana::error_handler($code, $error, $file, $line); + } + catch (Exception $e) + { + $this->assertEquals($code, $e->getCode()); + $this->assertEquals($error, $e->getMessage()); + } + error_reporting($error_level); + } + + /** + * Provides test data for test_modules_sets_and_returns_valid_modules() + * + * @return array + */ + public function provider_modules_detects_invalid_modules() + { + return array( + array(array('unittest' => MODPATH.'fo0bar')), + array(array('unittest' => MODPATH.'unittest', 'fo0bar' => MODPATH.'fo0bar')), + ); + } + + /** + * Tests Kohana::modules() + * + * @test + * @dataProvider provider_modules_detects_invalid_modules + * @expectedException Kohana_Exception + * @param boolean $source Input for Kohana::modules + * + */ + public function test_modules_detects_invalid_modules($source) + { + $modules = Kohana::modules(); + + try + { + Kohana::modules($source); + } + catch(Exception $e) + { + // Restore modules + Kohana::modules($modules); + + throw $e; + } + + // Restore modules + Kohana::modules($modules); + } + + /** + * Provides test data for test_modules_sets_and_returns_valid_modules() + * + * @return array + */ + public function provider_modules_sets_and_returns_valid_modules() + { + return array( + array(array(), array()), + array(array('unittest' => MODPATH.'unittest'), array('unittest' => $this->dirSeparator(MODPATH.'unittest/'))), + ); + } + + /** + * Tests Kohana::modules() + * + * @test + * @dataProvider provider_modules_sets_and_returns_valid_modules + * @param boolean $source Input for Kohana::modules + * @param boolean $expected Output for Kohana::modules + */ + public function test_modules_sets_and_returns_valid_modules($source, $expected) + { + $modules = Kohana::modules(); + + try + { + $this->assertEquals($expected, Kohana::modules($source)); + } + catch(Exception $e) + { + Kohana::modules($modules); + + throw $e; + } + + Kohana::modules($modules); + } + + /** + * To make the tests as portable as possible this just tests that + * you get an array of modules when you can Kohana::modules() and that + * said array contains unittest + * + * @test + * @covers Kohana::modules + */ + public function test_modules_returns_array_of_modules() + { + $modules = Kohana::modules(); + + $this->assertInternalType('array', $modules); + + $this->assertArrayHasKey('unittest', $modules); + } + + /** + * Tests Kohana::include_paths() + * + * The include paths must contain the apppath and syspath + * @test + * @covers Kohana::include_paths + */ + public function test_include_paths() + { + $include_paths = Kohana::include_paths(); + $modules = Kohana::modules(); + + $this->assertInternalType('array', $include_paths); + + // We must have at least 2 items in include paths (APP / SYS) + $this->assertGreaterThan(2, count($include_paths)); + // Make sure said paths are in the include paths + // And make sure they're in the correct positions + $this->assertSame(APPPATH, reset($include_paths)); + $this->assertSame(SYSPATH, end($include_paths)); + + foreach ($modules as $module) + { + $this->assertContains($module, $include_paths); + } + } +} + diff --git a/system/tests/kohana/DateTest.php b/system/tests/kohana/DateTest.php new file mode 100644 index 0000000..66c41ac --- /dev/null +++ b/system/tests/kohana/DateTest.php @@ -0,0 +1,790 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_DateTest extends Unittest_TestCase +{ + protected $_original_timezone = NULL; + + /** + * Ensures we have a consistant timezone for testing. + */ + // @codingStandardsIgnoreStart + public function setUp() + // @codingStandardsIgnoreEnd + { + parent::setUp(); + + $this->_original_timezone = date_default_timezone_get(); + + date_default_timezone_set('America/Chicago'); + } + + /** + * Restores original timezone after testing. + */ + // @codingStandardsIgnoreStart + public function tearDown() + // @codingStandardsIgnoreEnd + { + date_default_timezone_set($this->_original_timezone); + + parent::tearDown(); + } + + /** + * Provides test data for test_offset() + * + * @return array + */ + public function provider_offset() + { + return array( + array(30600, 'Asia/Calcutta', 'America/Argentina/Buenos_Aires'), + ); + } + + /** + * Tests Date::offset() + * + * @test + * @dataProvider provider_offset + * @covers Date::offset + * @param integer $expected Expected offset + * @param string $remote Remote TZ + * @param string $local Local TZ + * @param integer $now Current timestamp + */ + public function test_offset($expected, $remote, $local, $now = NULL) + { + $this->assertSame($expected, Date::offset($remote, $local, $now)); + } + + /** + * Provides test data for test_date() + * + * @return array + */ + public function provider_am_pm() + { + return array( + // All possible values + array(0, 'AM'), + array(1, 'AM'), + array(2, 'AM'), + array(3, 'AM'), + array(4, 'AM'), + array(5, 'AM'), + array(6, 'AM'), + array(7, 'AM'), + array(8, 'AM'), + array(9, 'AM'), + array(10, 'AM'), + array(11, 'AM'), + array(12, 'PM'), + array(13, 'PM'), + array(14, 'PM'), + array(15, 'PM'), + array(16, 'PM'), + array(17, 'PM'), + array(18, 'PM'), + array(19, 'PM'), + array(20, 'PM'), + array(21, 'PM'), + array(22, 'PM'), + array(23, 'PM'), + array(24, 'PM'), + // ampm doesn't validate the hour, so I don't think we should test it.. + // test strings are converted + array('0', 'AM'), + array('12', 'PM'), + ); + } + + /** + * Tests Date::ampm() + * + * @test + * @covers Date::ampm + * @dataProvider provider_am_pm + * @param $hour + * @param $expected + */ + public function test_am_pm($hour, $expected) + { + $this->assertSame( + $expected, + Date::ampm($hour) + ); + } + + /** + * Provides test data for test_adjust() + * + * @return array + */ + public function provider_adjust() + { + return array( + // Might as well test all possibilities + array(1, 'am', '01'), + array(2, 'am', '02'), + array(3, 'am', '03'), + array(4, 'am', '04'), + array(5, 'am', '05'), + array(6, 'am', '06'), + array(7, 'am', '07'), + array(8, 'am', '08'), + array(9, 'am', '09'), + array(10, 'am', '10'), + array(11, 'am', '11'), + array(12, 'am', '00'), + array(1, 'pm', '13'), + array(2, 'pm', '14'), + array(3, 'pm', '15'), + array(4, 'pm', '16'), + array(5, 'pm', '17'), + array(6, 'pm', '18'), + array(7, 'pm', '19'), + array(8, 'pm', '20'), + array(9, 'pm', '21'), + array(10, 'pm', '22'), + array(11, 'pm', '23'), + array(12, 'pm', '12'), + // It should also work with strings instead of ints + array('10', 'pm', '22'), + array('10', 'am', '10'), + ); + } + + /** + * Tests Date::ampm() + * + * @test + * @dataProvider provider_adjust + * @param integer $hour Hour in 12 hour format + * @param string $ampm Either am or pm + * @param string $expected Expected result + */ + public function test_adjust($hour, $ampm, $expected) + { + $this->assertSame( + $expected, + Date::adjust($hour, $ampm) + ); + } + + /** + * Provides test data for test_days() + * + * @return array + */ + public function provider_days() + { + return array( + // According to "the rhyme" these should be the same every year + array(9, FALSE, 30), + array(4, FALSE, 30), + array(6, FALSE, 30), + array(11, FALSE, 30), + array(1, FALSE, 31), + array(3, FALSE, 31), + array(5, FALSE, 31), + array(7, FALSE, 31), + array(8, FALSE, 31), + array(10, FALSE, 31), + // February is such a pain + array(2, 2001, 28), + array(2, 2000, 29), + array(2, 2012, 29), + ); + } + + /** + * Tests Date::days() + * + * @test + * @covers Date::days + * @dataProvider provider_days + * @param integer $month + * @param integer $year + * @param integer $expected + */ + public function test_days($month, $year, $expected) + { + $days = Date::days($month, $year); + + $this->assertSame( + $expected, + count($days) + ); + + // This should be a mirrored array, days => days + for ($i = 1; $i <= $expected; ++$i) + { + $this->assertArrayHasKey($i, $days); + // Combining the type check into this saves about 400-500 assertions! + $this->assertSame( (string) $i, $days[$i]); + } + } + + /** + * Provides test data for test_formatted_time() + * + * @return array + */ + public function provider_formatted_time() + { + return array( + // Test the default format + array('2010-04-16 17:00:00', '5:00PM 16th April 2010'), + // Now we use our own format + // Binary date! + array('01/01/2010 01:00', '1AM 1st January 2010', 'd/m/Y H:i'), + // Timezones (see #3902) + array('2011-04-01 01:23:45 Antarctica/South_Pole', '2011-04-01 01:23:45', 'Y-m-d H:i:s e', 'Antarctica/South_Pole'), + array('2011-04-01 01:23:45 Antarctica/South_Pole', '2011-03-31 14:23:45 Europe/Paris', 'Y-m-d H:i:s e', 'Antarctica/South_Pole'), + array('2011-04-01 01:23:45 Antarctica/South_Pole', '@1301574225', 'Y-m-d H:i:s e', 'Antarctica/South_Pole'), + ); + } + + /** + * Tests Date::formatted_time() + * + * @test + * @dataProvider provider_formatted_time + * @covers Date::formatted_time + * @ticket 3035 3902 + * @param string $expected Expected output + * @param string|integer $datetime_str The datetime timestamp / string + * @param string|null $timestamp_format The output format + * @param string|null $timezone The timezone identifier + */ + public function test_formatted_time($expected, $datetime_str, $timestamp_format = NULL, $timezone = NULL) + { + $timestamp = Date::formatted_time($datetime_str, $timestamp_format, $timezone); + + $this->assertSame($expected, $timestamp); + } + + /** + * Provider for test_months() + * + * @return array Test data + */ + public function provider_months() + { + return array( + array( + array( + 1 => "1", + 2 => "2", + 3 => "3", + 4 => "4", + 5 => "5", + 6 => "6", + 7 => "7", + 8 => "8", + 9 => "9", + 10 => "10", + 11 => "11", + 12 => "12" + ), + NULL + ), + array( + array( + 1 => "1", + 2 => "2", + 3 => "3", + 4 => "4", + 5 => "5", + 6 => "6", + 7 => "7", + 8 => "8", + 9 => "9", + 10 => "10", + 11 => "11", + 12 => "12" + ), + 'Guinness' + ), + array( + array( + 1 => "January", + 2 => "February", + 3 => "March", + 4 => "April", + 5 => "May", + 6 => "June", + 7 => "July", + 8 => "August", + 9 => "September", + 10 => "October", + 11 => "November", + 12 => "December" + ), + Date::MONTHS_LONG + ), + array( + array( + 1 => "Jan", + 2 => "Feb", + 3 => "Mar", + 4 => "Apr", + 5 => "May", + 6 => "Jun", + 7 => "Jul", + 8 => "Aug", + 9 => "Sep", + 10 => "Oct", + 11 => "Nov", + 12 => "Dec" + ), + Date::MONTHS_SHORT + ) + + ); + } + + /** + * Date::months() should allow the user to specify different format types, defaulting + * to a mirrored month number => month number array if format is NULL or unrecognised + * + * @test + * @dataProvider provider_months + * @covers Date::months + */ + public function test_months($expected, $format) + { + $months = Date::months($format); + + $this->assertSame($expected, $months); + } + + /** + * Provides test data for test_span() + * + * @return array + */ + public function provider_span() + { + $time = time(); + return array( + // Test that it must specify an output format + array( + $time, + $time, + '', + FALSE + ), + // Test that providing only one output just returns that output + array( + $time - 30, + $time, + 'seconds', + 30 + ), + // Random tests + array( + $time - 30, + $time, + 'years,months,weeks,days,hours,minutes,seconds', + array('years' => 0, 'months' => 0, 'weeks' => 0, 'days' => 0, 'hours' => 0, 'minutes' => 0, 'seconds' => 30), + ), + array( + $time - (60 * 60 * 24 * 782) + (60 * 25), + $time, + 'years,months,weeks,days,hours,minutes,seconds', + array('years' => 2, 'months' => 1, 'weeks' => 3, 'days' => 0, 'hours' => 1, 'minutes' => 28, 'seconds' => 24), + ), + // Should be able to compare with the future & that it only uses formats specified + array( + $time + (60 * 60 * 24 * 15) + (60 * 5), + $time, + 'weeks,days,hours,minutes,seconds', + array('weeks' => 2, 'days' => 1, 'hours' => 0, 'minutes' => 5, 'seconds' => 0), + ), + array( + // Add a bit of extra time to account for phpunit processing + $time + (14 * 31 * 24* 60 * 60) + (79 * 80), + NULL, + 'months,years', + array('months' => 2, 'years' => 1), + ), + ); + } + + /** + * Tests Date::span() + * + * @test + * @covers Date::span + * @dataProvider provider_span + * @param integer $time1 Time in the past + * @param integer $time2 Time to compare against + * @param string $output Units to output + * @param array $expected Array of $outputs => values + */ + public function test_span($time1, $time2, $output, $expected) + { + $this->assertSame( + $expected, + Date::span($time1, $time2, $output) + ); + } + + /** + * Provides test data to test_fuzzy_span + * + * This test data is provided on the assumption that it + * won't take phpunit more than 30 seconds to get the + * data from this provider to the test... ;) + * + * @return array Test Data + */ + public function provider_fuzzy_span() + { + $now = time(); + + return array( + array('moments ago', $now - 30, $now), + array('in moments', $now + 30, $now), + + array('a few minutes ago', $now - 10*60, $now), + array('in a few minutes', $now + 10*60, $now), + + array('less than an hour ago', $now - 45*60, $now), + array('in less than an hour', $now + 45*60, $now), + + array('a couple of hours ago', $now - 2*60*60, $now), + array('in a couple of hours', $now + 2*60*60, $now), + + array('less than a day ago', $now - 12*60*60, $now), + array('in less than a day', $now + 12*60*60, $now), + + array('about a day ago', $now - 30*60*60, $now), + array('in about a day', $now + 30*60*60, $now), + + array('a couple of days ago', $now - 3*24*60*60, $now), + array('in a couple of days', $now + 3*24*60*60, $now), + + array('less than a week ago', $now - 5*24*60*60, $now), + array('in less than a week', $now + 5*24*60*60, $now), + + array('about a week ago', $now - 9*24*60*60, $now), + array('in about a week', $now + 9*24*60*60, $now), + + array('less than a month ago', $now - 20*24*60*60, $now), + array('in less than a month', $now + 20*24*60*60, $now), + + array('about a month ago', $now - 40*24*60*60, $now), + array('in about a month', $now + 40*24*60*60, $now), + + array('a couple of months ago', $now - 3*30*24*60*60, $now), + array('in a couple of months', $now + 3*30*24*60*60, $now), + + array('less than a year ago', $now - 7*31*24*60*60, $now), + array('in less than a year', $now + 7*31*24*60*60, $now), + + array('about a year ago', $now - 18*31*24*60*60, $now), + array('in about a year', $now + 18*31*24*60*60, $now), + + array('a couple of years ago', $now - 3*12*31*24*60*60, $now), + array('in a couple of years', $now + 3*12*31*24*60*60, $now), + + array('a few years ago', $now - 5*12*31*24*60*60, $now), + array('in a few years', $now + 5*12*31*24*60*60, $now), + + array('about a decade ago', $now - 11*12*31*24*60*60, $now), + array('in about a decade', $now + 11*12*31*24*60*60, $now), + + array('a couple of decades ago', $now - 20*12*31*24*60*60, $now), + array('in a couple of decades', $now + 20*12*31*24*60*60, $now), + + array('several decades ago', $now - 50*12*31*24*60*60, $now), + array('in several decades', $now + 50*12*31*24*60*60, $now), + + array('a long time ago', $now - pow(10,10), $now), + array('in a long time', $now + pow(10,10), $now), + ); + } + + /** + * Test of Date::fuzy_span() + * + * @test + * @dataProvider provider_fuzzy_span + * @param string $expected Expected output + * @param integer $timestamp Timestamp to use + * @param integer $local_timestamp The local timestamp to use + */ + public function test_fuzzy_span($expected, $timestamp, $local_timestamp) + { + $this->assertSame( + $expected, + Date::fuzzy_span($timestamp, $local_timestamp) + ); + } + + /** + * Provides test data for test_years() + * + * @return array Test Data + */ + public function provider_years() + { + return array( + array( + array ( + 2005 => '2005', + 2006 => '2006', + 2007 => '2007', + 2008 => '2008', + 2009 => '2009', + 2010 => '2010', + 2011 => '2011', + 2012 => '2012', + 2013 => '2013', + 2014 => '2014', + 2015 => '2015', + ), + 2005, + 2015 + ), + ); + } + + /** + * Tests Data::years() + * + * @test + * @dataProvider provider_years + */ + public function test_years($expected, $start = FALSE, $end = FALSE) + { + $this->assertSame( + $expected, + Date::years($start, $end) + ); + } + + public function provider_hours() + { + return array( + array( + array( + 1 => '1', + 2 => '2', + 3 => '3', + 4 => '4', + 5 => '5', + 6 => '6', + 7 => '7', + 8 => '8', + 9 => '9', + 10 => '10', + 11 => '11', + 12 => '12', + ), + ), + ); + } + + /** + * Test for Date::hours + * + * @test + * @dataProvider provider_hours + */ + public function test_hours($expected, $step = 1, $long = FALSE, $start = NULL) + { + $this->assertSame( + $expected, + Date::hours($step, $long, $start) + ); + } + + /** + * Provides test data for test_seconds + * + * @return array Test data + */ + public function provider_seconds() + { + return array( + array( + // Thank god for var_export() + array ( + 0 => '00', 1 => '01', 2 => '02', 3 => '03', 4 => '04', + 5 => '05', 6 => '06', 7 => '07', 8 => '08', 9 => '09', + 10 => '10', 11 => '11', 12 => '12', 13 => '13', 14 => '14', + 15 => '15', 16 => '16', 17 => '17', 18 => '18', 19 => '19', + 20 => '20', 21 => '21', 22 => '22', 23 => '23', 24 => '24', + 25 => '25', 26 => '26', 27 => '27', 28 => '28', 29 => '29', + 30 => '30', 31 => '31', 32 => '32', 33 => '33', 34 => '34', + 35 => '35', 36 => '36', 37 => '37', 38 => '38', 39 => '39', + 40 => '40', 41 => '41', 42 => '42', 43 => '43', 44 => '44', + 45 => '45', 46 => '46', 47 => '47', 48 => '48', 49 => '49', + 50 => '50', 51 => '51', 52 => '52', 53 => '53', 54 => '54', + 55 => '55', 56 => '56', 57 => '57', 58 => '58', 59 => '59', + ), + 1, + 0, + 60 + ), + ); + } + + /** + * + * @test + * @dataProvider provider_seconds + * @covers Date::seconds + */ + public function test_seconds($expected, $step = 1, $start = 0, $end = 60) + { + $this->assertSame( + $expected, + Date::seconds($step, $start, $end) + ); + } + + /** + * Provides test data for test_minutes + * + * @return array Test data + */ + public function provider_minutes() + { + return array( + array( + array( + 0 => '00', 5 => '05', 10 => '10', + 15 => '15', 20 => '20', 25 => '25', + 30 => '30', 35 => '35', 40 => '40', + 45 => '45', 50 => '50', 55 => '55', + ), + 5, + ), + ); + } + + /** + * + * @test + * @dataProvider provider_minutes + */ + public function test_minutes($expected, $step) + { + $this->assertSame( + $expected, + Date::minutes($step) + ); + } + + /** + * This tests that the minutes helper defaults to using a $step of 5 + * and thus returns an array of 5 minute itervals + * + * @test + * @covers Date::minutes + */ + public function test_minutes_defaults_to_using_step_of5() + { + $minutes = array( + 0 => '00', 5 => '05', 10 => '10', + 15 => '15', 20 => '20', 25 => '25', + 30 => '30', 35 => '35', 40 => '40', + 45 => '45', 50 => '50', 55 => '55', + ); + + $this->assertSame( + $minutes, + Date::minutes() + ); + } + + /** + * Provids for test_unix2dos + * + * @return array Test Data + */ + public function provider_unix2dos() + { + return array( + array( + 1024341746, + 1281786936 + ), + array( + 2162688, + 315554400 + ) + ); + } + + /** + * Test Date::unix2dos() + * + * You should always pass a timestamp as otherwise the current + * date/time would be used and that's oviously variable + * + * Geert seems to be the only person who knows how unix2dos() works + * so we just throw in some random values and see what happens + * + * @test + * @dataProvider provider_unix2dos + * @covers Date::unix2dos + * @param integer $expected Expected output + * @param integer $timestamp Input timestamp + */ + public function test_unix2dos($expected, $timestamp) + { + $this->assertSame($expected, Date::unix2dos($timestamp)); + } + + /** + * Provides test data for test_dos2unix + * + * @return array Test data + */ + public function provider_dos2unix() + { + return array( + array( + 1281786936, + 1024341746, + ), + array( + 315554400, + 2162688, + ), + ); + } + + /** + * Tests Date::dos2unix + * + * @test + * @dataProvider provider_dos2unix + * @param integer $expected Expected output + * @param integer $timestamp Input timestamp + */ + public function test_dos2unix($expected, $timestamp) + { + $this->assertEquals($expected, Date::dos2unix($timestamp)); + } +} diff --git a/system/tests/kohana/DebugTest.php b/system/tests/kohana/DebugTest.php new file mode 100644 index 0000000..39176ec --- /dev/null +++ b/system/tests/kohana/DebugTest.php @@ -0,0 +1,126 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaphp.com/license + */ +class Kohana_DebugTest extends Unittest_TestCase +{ + + /** + * Provides test data for test_debug() + * + * @return array + */ + public function provider_vars() + { + return array( + // $thing, $expected + array(array('foobar'), "
      array(1) (\n    0 => string(6) \"foobar\"\n)
      "), + ); + } + + /** + * Tests Debug::vars() + * + * @test + * @dataProvider provider_vars + * @covers Debug::vars + * @param boolean $thing The thing to debug + * @param boolean $expected Output for Debug::vars + */ + public function test_var($thing, $expected) + { + $this->assertEquals($expected, Debug::vars($thing)); + } + + /** + * Provides test data for testDebugPath() + * + * @return array + */ + public function provider_debug_path() + { + return array( + array( + SYSPATH.'classes'.DIRECTORY_SEPARATOR.'kohana'.EXT, + 'SYSPATH'.DIRECTORY_SEPARATOR.'classes'.DIRECTORY_SEPARATOR.'kohana.php' + ), + array( + MODPATH.$this->dirSeparator('unittest/classes/kohana/unittest/runner').EXT, + $this->dirSeparator('MODPATH/unittest/classes/kohana/unittest/runner').EXT + ), + ); + } + + /** + * Tests Debug::path() + * + * @test + * @dataProvider provider_debug_path + * @covers Debug::path + * @param boolean $path Input for Debug::path + * @param boolean $expected Output for Debug::path + */ + public function test_debug_path($path, $expected) + { + $this->assertEquals($expected, Debug::path($path)); + } + + /** + * Provides test data for test_dump() + * + * @return array + */ + public function provider_dump() + { + return array( + array('foobar', 128, 10, 'string(6) "foobar"'), + array('foobar', 2, 10, 'string(6) "fo …"'), + array(NULL, 128, 10, 'NULL'), + array(TRUE, 128, 10, 'bool TRUE'), + array(array('foobar'), 128, 10, "array(1) (\n 0 => string(6) \"foobar\"\n)"), + array(new StdClass, 128, 10, "object stdClass(0) {\n}"), + array("fo\x6F\xFF\x00bar\x8F\xC2\xB110", 128, 10, 'string(10) "foobar±10"'), + array(array('level1' => array('level2' => array('level3' => array('level4' => array('value' => 'something'))))), 128, 4, +'array(1) ( + "level1" => array(1) ( + "level2" => array(1) ( + "level3" => array(1) ( + "level4" => array(1) ( + ... + ) + ) + ) + ) +)'), + ); + } + + /** + * Tests Debug::dump() + * + * @test + * @dataProvider provider_dump + * @covers Debug::dump + * @covers Debug::_dump + * @param object $exception exception to test + * @param string $expected expected output + */ + public function test_dump($input, $length, $limit, $expected) + { + $this->assertEquals($expected, Debug::dump($input, $length, $limit)); + } +} diff --git a/system/tests/kohana/ExceptionTest.php b/system/tests/kohana/ExceptionTest.php new file mode 100644 index 0000000..7c35afb --- /dev/null +++ b/system/tests/kohana/ExceptionTest.php @@ -0,0 +1,99 @@ + 'b')), 'b', 0), + array(array(':a :b', array(':a' => 'c', ':b' => 'd')), 'c d', 0), + + array(array(':a', NULL, 5), ':a', 5), + // #3358 + array(array(':a', NULL, '3F000'), ':a', '3F000'), + // #3404 + array(array(':a', NULL, '42S22'), ':a', '42S22'), + // #3927 + array(array(':a', NULL, 'b'), ':a', 'b'), + // #4039 + array(array(':a', NULL, '25P01'), ':a', '25P01'), + ); + } + + /** + * Tests Kohana_Kohana_Exception::__construct() + * + * @test + * @dataProvider provider_constructor + * @covers Kohana_Kohana_Exception::__construct + * @param array $arguments Arguments + * @param string $expected_message Value from getMessage() + * @param integer|string $expected_code Value from getCode() + */ + public function test_constructor($arguments, $expected_message, $expected_code) + { + switch (count($arguments)) + { + case 1: + $exception = new Kohana_Exception(reset($arguments)); + break; + case 2: + $exception = new Kohana_Exception(reset($arguments), next($arguments)); + break; + default: + $exception = new Kohana_Exception(reset($arguments), next($arguments), next($arguments)); + } + + $this->assertSame($expected_code, $exception->getCode()); + $this->assertSame($expected_message, $exception->getMessage()); + } + + /** + * Provides test data for test_text() + * + * @return array + */ + public function provider_text() + { + return array( + array(new Kohana_Exception('foobar'), $this->dirSeparator('Kohana_Exception [ 0 ]: foobar ~ SYSPATH/tests/kohana/ExceptionTest.php [ '.__LINE__.' ]')), + ); + } + + /** + * Tests Kohana_Exception::text() + * + * @test + * @dataProvider provider_text + * @covers Kohana_Exception::text + * @param object $exception exception to test + * @param string $expected expected output + */ + public function test_text($exception, $expected) + { + $this->assertEquals($expected, Kohana_Exception::text($exception)); + } +} diff --git a/system/tests/kohana/FeedTest.php b/system/tests/kohana/FeedTest.php new file mode 100644 index 0000000..4098630 --- /dev/null +++ b/system/tests/kohana/FeedTest.php @@ -0,0 +1,123 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_FeedTest extends Unittest_TestCase +{ + /** + * Provides test data for test_parse() + * + * @return array + */ + public function provider_parse() + { + return array( + // $source, $expected + array('http://dev.kohanaframework.org/projects/kohana3/activity.atom', 15), + ); + } + + /** + * Tests that Feed::parse gets the correct number of elements + * + * @test + * @dataProvider provider_parse + * @covers feed::parse + * @param string $source URL to test + * @param integer $expected Count of items + */ + public function test_parse($source, $expected) + { + $this->markTestSkipped('We don\'t go to the internet for tests.'); + + $this->assertEquals($expected, count(Feed::parse($source))); + } + + /** + * Provides test data for test_create() + * + * @return array + */ + public function provider_create() + { + $info = array('pubDate' => 123, 'image' => array('link' => 'http://kohanaframework.org/image.png', 'url' => 'http://kohanaframework.org/', 'title' => 'title')); + + return array( + // $source, $expected + array($info, array('foo' => array('foo' => 'bar', 'pubDate' => 123, 'link' => 'foo')), array('_SERVER' => array('HTTP_HOST' => 'localhost')+$_SERVER), + array( + 'tag' => 'channel', + 'descendant' => array( + 'tag' => 'item', + 'child' => array( + 'tag' => 'foo', + 'content' => 'bar' + ) + ) + ), + array( + $this->matcher_composer($info, 'image', 'link'), + $this->matcher_composer($info, 'image', 'url'), + $this->matcher_composer($info, 'image', 'title') + ) + ), + ); + } + + /** + * Helper for handy matcher composing + * + * @param array $data + * @param string $tag + * @param string $child + * @return array + */ + private function matcher_composer($data, $tag, $child) + { + return array( + 'tag' => 'channel', + 'descendant' => array( + 'tag' => $tag, + 'child' => array( + 'tag' => $child, + 'content' => $data[$tag][$child] + ) + ) + ); + } + + /** + * @test + * + * @dataProvider provider_create + * + * @covers feed::create + * + * @param string $info info to pass + * @param integer $items items to add + * @param integer $matcher output + */ + public function test_create($info, $items, $enviroment, $matcher_item, $matchers_image) + { + $this->setEnvironment($enviroment); + + $this->assertTag($matcher_item, Feed::create($info, $items), '', FALSE); + + foreach ($matchers_image as $matcher_image) + { + $this->assertTag($matcher_image, Feed::create($info, $items), '', FALSE); + } + } +} diff --git a/system/tests/kohana/FileTest.php b/system/tests/kohana/FileTest.php new file mode 100644 index 0000000..8d4c491 --- /dev/null +++ b/system/tests/kohana/FileTest.php @@ -0,0 +1,81 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_FileTest extends Unittest_TestCase +{ + /** + * Provides test data for test_sanitize() + * + * @return array + */ + public function provider_mime() + { + return array( + // $value, $result + array(Kohana::find_file('classes', 'File')), + array(Kohana::find_file('tests', 'test_data/github', 'png')), + ); + } + + /** + * Tests File::mime() + * + * @test + * @dataProvider provider_mime + * @param boolean $input Input for File::mime + * @param boolean $expected Output for File::mime + */ + public function test_mime($input) + { + $this->markTestSkipped( + 'This test doesn\'t do anything useful!' + ); + $this->assertSame(1, preg_match('/^(?:application|audio|image|message|multipart|text|video)\/[a-z.+0-9-]+$/i', File::mime($input))); + } + + /** + * Provides test data for test_split_join() + * + * @return array + */ + public function provider_split_join() + { + return array( + // $value, $result + array(Kohana::find_file('tests', 'test_data/github', 'png'), .01, 1), + ); + } + + /** + * Tests File::mime() + * + * @test + * @dataProvider provider_split_join + * @param boolean $input Input for File::split + * @param boolean $peices Input for File::split + * @param boolean $expected Output for File::splut + */ + public function test_split_join($input, $peices, $expected) + { + $this->assertSame($expected, File::split($input, $peices)); + $this->assertSame($expected, File::join($input)); + + foreach (glob(Kohana::find_file('tests', 'test_data/github', 'png').'.*') as $file) + { + unlink($file); + } + } +} diff --git a/system/tests/kohana/FormTest.php b/system/tests/kohana/FormTest.php new file mode 100644 index 0000000..a3ade94 --- /dev/null +++ b/system/tests/kohana/FormTest.php @@ -0,0 +1,408 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_FormTest extends Unittest_TestCase +{ + /** + * Defaults for this test + * @var array + */ + // @codingStandardsIgnoreStart + protected $environmentDefault = array( + 'Kohana::$base_url' => '/', + 'HTTP_HOST' => 'kohanaframework.org', + 'Kohana::$index_file' => '', + ); + // @codingStandardsIgnoreEnd + + /** + * Provides test data for test_open() + * + * @return array + */ + public function provider_open() + { + return array( + array( + array('', NULL), + array('action' => '') + ), + array( + array(NULL, NULL), + array('action' => '') + ), + array( + array('foo', NULL), + array('action' => '/foo') + ), + array( + array('foo', array('method' => 'get')), + array('action' => '/foo', 'method' => 'get') + ), + ); + } + + /** + * Tests Form::open() + * + * @test + * @dataProvider provider_open + * @param boolean $input Input for Form::open + * @param boolean $expected Output for Form::open + */ + public function test_open($input, $expected) + { + list($action, $attributes) = $input; + + $tag = Form::open($action, $attributes); + + $matcher = array( + 'tag' => 'form', + // Default attributes + 'attributes' => array( + 'method' => 'post', + 'accept-charset' => 'utf-8', + ), + ); + + $matcher['attributes'] = $expected + $matcher['attributes']; + + $this->assertTag($matcher, $tag); + } + + /** + * Tests Form::close() + * + * @test + */ + public function test_close() + { + $this->assertSame('', Form::close()); + } + + /** + * Provides test data for test_input() + * + * @return array + */ + public function provider_input() + { + return array( + // $value, $result + array('input', 'foo', 'bar', NULL, 'input'), + array('input', 'foo', NULL, NULL, 'input'), + array('hidden', 'foo', 'bar', NULL, 'hidden'), + array('password', 'foo', 'bar', NULL, 'password'), + ); + } + + /** + * Tests Form::input() + * + * @test + * @dataProvider provider_input + * @param boolean $input Input for Form::input + * @param boolean $expected Output for Form::input + */ + public function test_input($type, $name, $value, $attributes) + { + $matcher = array( + 'tag' => 'input', + 'attributes' => array('name' => $name, 'type' => $type) + ); + + // Form::input creates a text input + if ($type === 'input') + { + $matcher['attributes']['type'] = 'text'; + } + + // NULL just means no value + if ($value !== NULL) + { + $matcher['attributes']['value'] = $value; + } + + // Add on any attributes + if (is_array($attributes)) + { + $matcher['attributes'] = $attributes + $matcher['attributes']; + } + + $tag = Form::$type($name, $value, $attributes); + + $this->assertTag($matcher, $tag, $tag); + } + + /** + * Provides test data for test_file() + * + * @return array + */ + public function provider_file() + { + return array( + // $value, $result + array('foo', NULL, ''), + ); + } + + /** + * Tests Form::file() + * + * @test + * @dataProvider provider_file + * @param boolean $input Input for Form::file + * @param boolean $expected Output for Form::file + */ + public function test_file($name, $attributes, $expected) + { + $this->assertSame($expected, Form::file($name, $attributes)); + } + + /** + * Provides test data for test_check() + * + * @return array + */ + public function provider_check() + { + return array( + // $value, $result + array('checkbox', 'foo', NULL, FALSE, NULL), + array('checkbox', 'foo', NULL, TRUE, NULL), + array('checkbox', 'foo', 'bar', TRUE, NULL), + + array('radio', 'foo', NULL, FALSE, NULL), + array('radio', 'foo', NULL, TRUE, NULL), + array('radio', 'foo', 'bar', TRUE, NULL), + ); + } + + /** + * Tests Form::check() + * + * @test + * @dataProvider provider_check + * @param boolean $input Input for Form::check + * @param boolean $expected Output for Form::check + */ + public function test_check($type, $name, $value, $checked, $attributes) + { + $matcher = array('tag' => 'input', 'attributes' => array('name' => $name, 'type' => $type)); + + if ($value !== NULL) + { + $matcher['attributes']['value'] = $value; + } + + if (is_array($attributes)) + { + $matcher['attributes'] = $attributes + $matcher['attributes']; + } + + if ($checked === TRUE) + { + $matcher['attributes']['checked'] = 'checked'; + } + + $tag = Form::$type($name, $value, $checked, $attributes); + $this->assertTag($matcher, $tag, $tag); + } + + /** + * Provides test data for test_text() + * + * @return array + */ + public function provider_text() + { + return array( + // $value, $result + array('textarea', 'foo', 'bar', NULL), + array('textarea', 'foo', 'bar', array('rows' => 20, 'cols' => 20)), + array('button', 'foo', 'bar', NULL), + array('label', 'foo', 'bar', NULL), + array('label', 'foo', NULL, NULL), + ); + } + + /** + * Tests Form::textarea() + * + * @test + * @dataProvider provider_text + * @param boolean $input Input for Form::textarea + * @param boolean $expected Output for Form::textarea + */ + public function test_text($type, $name, $body, $attributes) + { + $matcher = array( + 'tag' => $type, + 'attributes' => array(), + 'content' => $body, + ); + + if ($type !== 'label') + { + $matcher['attributes'] = array('name' => $name); + } + else + { + $matcher['attributes'] = array('for' => $name); + } + + + if (is_array($attributes)) + { + $matcher['attributes'] = $attributes + $matcher['attributes']; + } + + $tag = Form::$type($name, $body, $attributes); + + $this->assertTag($matcher, $tag, $tag); + } + + + /** + * Provides test data for test_select() + * + * @return array + */ + public function provider_select() + { + return array( + // $value, $result + array('foo', NULL, NULL, ""), + array('foo', array('bar' => 'bar'), NULL, ""), + array('foo', array('bar' => 'bar'), 'bar', ""), + array('foo', array('bar' => array('foo' => 'bar')), NULL, ""), + array('foo', array('bar' => array('foo' => 'bar')), 'foo', ""), + // #2286 + array('foo', array('bar' => 'bar', 'unit' => 'test', 'foo' => 'foo'), array('bar', 'foo'), ""), + ); + } + + /** + * Tests Form::select() + * + * @test + * @dataProvider provider_select + * @param boolean $input Input for Form::select + * @param boolean $expected Output for Form::select + */ + public function test_select($name, $options, $selected, $expected) + { + // Much more efficient just to assertSame() rather than assertTag() on each element + $this->assertSame($expected, Form::select($name, $options, $selected)); + } + + /** + * Provides test data for test_submit() + * + * @return array + */ + public function provider_submit() + { + return array( + // $value, $result + array('foo', 'Foobar!', ''), + ); + } + + /** + * Tests Form::submit() + * + * @test + * @dataProvider provider_submit + * @param boolean $input Input for Form::submit + * @param boolean $expected Output for Form::submit + */ + public function test_submit($name, $value, $expected) + { + $matcher = array( + 'tag' => 'input', + 'attributes' => array('name' => $name, 'type' => 'submit', 'value' => $value) + ); + + $this->assertTag($matcher, Form::submit($name, $value)); + } + + + /** + * Provides test data for test_image() + * + * @return array + */ + public function provider_image() + { + return array( + // $value, $result + array('foo', 'bar', array('src' => 'media/img/login.png'), ''), + ); + } + + /** + * Tests Form::image() + * + * @test + * @dataProvider provider_image + * @param boolean $name Input for Form::image + * @param boolean $value Input for Form::image + * @param boolean $attributes Input for Form::image + * @param boolean $expected Output for Form::image + */ + public function test_image($name, $value, $attributes, $expected) + { + $this->assertSame($expected, Form::image($name, $value, $attributes)); + } + + /** + * Provides test data for test_label() + * + * @return array + */ + function provider_label() + { + return array( + // $value, $result + // Single for provided + array('email', NULL, NULL, ''), + array('email_address', NULL, NULL, ''), + array('email-address', NULL, NULL, ''), + // For and text values provided + array('name', 'First name', NULL, ''), + // with attributes + array('lastname', 'Last name', array('class' => 'text'), ''), + array('lastname', 'Last name', array('class' => 'text', 'id'=>'txt_lastname'), ''), + ); + } + + /** + * Tests Form::label() + * + * @test + * @dataProvider provider_label + * @param boolean $for Input for Form::label + * @param boolean $text Input for Form::label + * @param boolean $attributes Input for Form::label + * @param boolean $expected Output for Form::label + */ + function test_label($for, $text, $attributes, $expected) + { + $this->assertSame($expected, Form::label($for, $text, $attributes)); + } +} diff --git a/system/tests/kohana/HTMLTest.php b/system/tests/kohana/HTMLTest.php new file mode 100644 index 0000000..015a65d --- /dev/null +++ b/system/tests/kohana/HTMLTest.php @@ -0,0 +1,367 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_HTMLTest extends Unittest_TestCase +{ + /** + * Defaults for this test + * @var array + */ + // @codingStandardsIgnoreStart + protected $environmentDefault = array( + 'Kohana::$base_url' => '/kohana/', + 'Kohana::$index_file' => 'index.php', + 'HTML::$strict' => TRUE, + 'HTTP_HOST' => 'www.kohanaframework.org', + ); + // @codingStandardsIgnoreStart + + /** + * Provides test data for test_attributes() + * + * @return array + */ + public function provider_attributes() + { + return array( + array( + array('name' => 'field', 'random' => 'not_quite', 'id' => 'unique_field'), + array(), + ' id="unique_field" name="field" random="not_quite"' + ), + array( + array('invalid' => NULL), + array(), + '' + ), + array( + array(), + array(), + '' + ), + array( + array('name' => 'field', 'checked'), + array(), + ' name="field" checked="checked"', + ), + array( + array('id' => 'disabled_field', 'disabled'), + array('HTML::$strict' => FALSE), + ' id="disabled_field" disabled', + ), + ); + } + + /** + * Tests HTML::attributes() + * + * @test + * @dataProvider provider_attributes + * @param array $attributes Attributes to use + * @param array $options Environment options to use + * @param string $expected Expected output + */ + public function test_attributes(array $attributes, array $options, $expected) + { + $this->setEnvironment($options); + + $this->assertSame( + $expected, + HTML::attributes($attributes) + ); + } + + /** + * Provides test data for test_script + * + * @return array Array of test data + */ + public function provider_script() + { + return array( + array( + '', + 'http://google.com/script.js', + ), + array( + '', + 'my/script.js', + NULL, + 'http', + TRUE + ), + array( + '', + 'my/script.js', + NULL, + 'https', + FALSE + ), + array( + '', + '/my/script.js', // Test absolute paths + NULL, + 'https', + FALSE + ), + + ); + } + + /** + * Tests HTML::script() + * + * @test + * @dataProvider provider_script + * @param string $expected Expected output + * @param string $file URL to script + * @param array $attributes HTML attributes for the anchor + * @param string $protocol Protocol to use + * @param bool $index Should the index file be included in url? + */ + public function test_script($expected, $file, array $attributes = NULL, $protocol = NULL, $index = FALSE) + { + $this->assertSame( + $expected, + HTML::script($file, $attributes, $protocol, $index) + ); + } + + /** + * Data provider for the style test + * + * @return array Array of test data + */ + public function provider_style() + { + return array( + array( + '', + 'http://google.com/style.css', + array(), + NULL, + FALSE + ), + array( + '', + 'my/style.css', + array(), + NULL, + FALSE + ), + array( + '', + 'my/style.css', + array(), + 'https', + FALSE + ), + array( + '', + 'my/style.css', + array(), + 'https', + TRUE + ), + array( + '', + '/my/style.css', + array(), + 'https', + TRUE + ), + array( + // #4283: http://dev.kohanaframework.org/issues/4283 + '', + 'my/style.css', + array( + 'rel' => 'stylesheet/less' + ), + 'https', + TRUE + ), + ); + } + + /** + * Tests HTML::style() + * + * @test + * @dataProvider provider_style + * @param string $expected The expected output + * @param string $file The file to link to + * @param array $attributes Any extra attributes for the link + * @param string $protocol Protocol to use + * @param bool $index Whether the index file should be added to the link + */ + public function test_style($expected, $file, array $attributes = NULL, $protocol = NULL, $index = FALSE) + { + $this->assertSame( + $expected, + HTML::style($file, $attributes, $protocol, $index) + ); + } + + /** + * Provides test data for test_anchor + * + * @return array Test data + */ + public function provider_anchor() + { + return array( + array( + '
      Kohana', + array(), + 'http://kohanaframework.org', + 'Kohana', + ), + array( + 'GOOGLE', + array(), + 'http://google.com', + 'GOOGLE', + array('target' => '_blank'), + 'http', + ), + array( + 'Kohana', + array(), + 'users/example', + 'Kohana', + NULL, + 'https', + FALSE, + ), + array( + 'Kohana', + array(), + 'users/example', + 'Kohana', + NULL, + 'https', + TRUE, + ), + array( + 'Kohana', + array(), + 'users/example', + 'Kohana', + NULL, + 'https', + ), + array( + 'Kohana', + array(), + 'users/example', + 'Kohana', + NULL, + 'https', + TRUE, + ), + array( + 'Kohana', + array(), + 'users/example', + 'Kohana', + NULL, + 'https', + FALSE, + ), + array( + 'Kohana', + array(), + '/users/example', + 'Kohana', + NULL, + 'https', + FALSE, + ), + ); + } + + /** + * Tests HTML::anchor + * + * @test + * @dataProvider provider_anchor + */ + public function test_anchor($expected, array $options, $uri, $title = NULL, array $attributes = NULL, $protocol = NULL, $index = TRUE) + { + // $this->setEnvironment($options); + + $this->assertSame( + $expected, + HTML::anchor($uri, $title, $attributes, $protocol, $index) + ); + } + + /** + * Data provider for test_file_anchor + * + * @return array + */ + public function provider_file_anchor() + { + return array( + array( + 'My picture file', + array(), + 'mypic.png', + 'My picture file', + ), + array( + 'My picture file', + array('attr' => 'value'), + 'mypic.png', + 'My picture file', + 'https', + TRUE + ), + array( + 'My picture file', + array(), + 'mypic.png', + 'My picture file', + 'ftp', + FALSE + ), + array( + 'My picture file', + array(), + '/mypic.png', + 'My picture file', + 'ftp', + FALSE + ), + ); + } + + /** + * Test for HTML::file_anchor() + * + * @test + * @covers HTML::file_anchor + * @dataProvider provider_file_anchor + */ + public function test_file_anchor($expected, array $attributes, $file, $title = NULL, $protocol = NULL, $index = FALSE) + { + $this->assertSame( + $expected, + HTML::file_anchor($file, $title, $attributes, $protocol, $index) + ); + } +} diff --git a/system/tests/kohana/HTTPTest.php b/system/tests/kohana/HTTPTest.php new file mode 100644 index 0000000..0f80548 --- /dev/null +++ b/system/tests/kohana/HTTPTest.php @@ -0,0 +1,87 @@ + '/kohana/', + 'Kohana::$index_file' => 'index.php', + 'HTTP_HOST' => 'www.example.com', + ); + // @codingStandardsIgnoreEnd + + /** + * Provides test data for test_attributes() + * + * @return array + */ + public function provider_redirect() + { + return array( + array( + 'http://www.example.org/', + 301, + 'HTTP_Exception_301', + 'http://www.example.org/' + ), + array( + '/page_one', + 302, + 'HTTP_Exception_302', + 'http://www.example.com/kohana/index.php/page_one' + ), + array( + 'page_two', + 303, + 'HTTP_Exception_303', + 'http://www.example.com/kohana/index.php/page_two' + ), + ); + } + + /** + * Tests HTTP::redirect() + * + * @test + * @dataProvider provider_redirect + * @param array $location Location to redirect to + * @param array $code HTTP Code to use for the redirect + * @param string $expected_exception Expected exception + * @param string $expected_location Expected exception + */ + public function test_redirect($location, $code, $expected_exception, $expected_location) + { + try + { + HTTP::redirect($location, $code); + } + catch (HTTP_Exception_Redirect $e) + { + $response = $e->get_response(); + + $this->assertInstanceOf($expected_exception, $e); + $this->assertEquals($expected_location, $response->headers('Location')); + + return; + } + + $this->fail('HTTP_Exception_Redirect not thrown'); + } +} diff --git a/system/tests/kohana/Http/HeaderTest.php b/system/tests/kohana/Http/HeaderTest.php new file mode 100644 index 0000000..0894b46 --- /dev/null +++ b/system/tests/kohana/Http/HeaderTest.php @@ -0,0 +1,1491 @@ + (float) 1, + 'text/plain' => 0.5, + 'application/json' => 0.1, + 'text/*' => (float) 1 + ) + ), + array( + array( + 'text/*', + 'text/html; level=1; q=0.4', + 'application/xml+rss; q=0.5; level=4' + ), + array( + 'text/*' => (float) 1, + 'text/html; level=1' => 0.4, + 'application/xml+rss; level=4' => 0.5 + ) + ) + ); + } + + /** + * Tests the `accept_quality` method parses the quality values + * correctly out of header parts + * + * @dataProvider provider_accept_quality + * + * @param array $parts input + * @param array $expected expected output + * @return void + */ + public function test_accept_quality(array $parts, array $expected) + { + $out = HTTP_Header::accept_quality($parts); + + foreach ($out as $key => $value) + { + $this->assertInternalType('float', $value); + } + + $this->assertSame($expected, $out); + } + + /** + * Data provider for test_parse_accept_header + * + * @return array + */ + public function provider_parse_accept_header() + { + return array( + array( + 'text/html, text/plain, text/*, */*', + array( + 'text' => array( + 'html' => (float) 1, + 'plain' => (float) 1, + '*' => (float) 1 + ), + '*' => array( + '*' => (float) 1 + ) + ) + ), + array( + 'text/html; q=.5, application/json, application/xml+rss; level=1; q=.7, text/*, */*', + array( + 'text' => array( + 'html' => 0.5, + '*' => (float) 1 + ), + 'application' => array( + 'json' => (float) 1, + 'xml+rss; level=1' => 0.7 + ), + '*' => array( + '*' => (float) 1 + ) + ) + ) + ); + } + + /** + * Tests the `parse_accept_header` method parses the Accept: header + * correctly and returns expected output + * + * @dataProvider provider_parse_accept_header + * + * @param string $accept accept in + * @param array $expected expected out + * @return void + */ + public function test_parse_accept_header($accept, array $expected) + { + $this->assertSame($expected, HTTP_Header::parse_accept_header($accept)); + } + + /** + * Provides data for test_parse_charset_header + * + * @return array + */ + public function provider_parse_charset_header() + { + return array( + array( + 'utf-8, utf-10, utf-16, iso-8859-1', + array( + 'utf-8' => (float) 1, + 'utf-10' => (float) 1, + 'utf-16' => (float) 1, + 'iso-8859-1'=> (float) 1 + ) + ), + array( + 'utf-8, utf-10; q=.9, utf-16; q=.5, iso-8859-1; q=.75', + array( + 'utf-8' => (float) 1, + 'utf-10' => 0.9, + 'utf-16' => 0.5, + 'iso-8859-1'=> 0.75 + ) + ), + array( + NULL, + array( + '*' => (float) 1 + ) + ) + ); + } + + /** + * Tests the `parse_charset_header` method parsed the Accept-Charset header + * correctly + * + * @dataProvider provider_parse_charset_header + * + * @param string $accept accept + * @param array $expected expected + * @return void + */ + public function test_parse_charset_header($accept, array $expected) + { + $this->assertSame($expected, HTTP_Header::parse_charset_header($accept)); + } + + /** + * Provides data for test_parse_charset_header + * + * @return array + */ + public function provider_parse_encoding_header() + { + return array( + array( + 'compress, gzip, blowfish', + array( + 'compress' => (float) 1, + 'gzip' => (float) 1, + 'blowfish' => (float) 1 + ) + ), + array( + 'compress, gzip; q=0.12345, blowfish; q=1.0', + array( + 'compress' => (float) 1, + 'gzip' => 0.12345, + 'blowfish' => (float) 1 + ) + ), + array( + NULL, + array( + '*' => (float) 1 + ) + ), + array( + '', + array( + 'identity' => (float) 1 + ) + ) + ); + } + + /** + * Tests the `parse_encoding_header` method parses the Accept-Encoding header + * correctly + * + * @dataProvider provider_parse_encoding_header + * + * @param string $accept accept + * @param array $expected expected + * @return void + */ + public function test_parse_encoding_header($accept, array $expected) + { + $this->assertSame($expected, HTTP_Header::parse_encoding_header($accept)); + } + + /** + * Provides data for test_parse_charset_header + * + * @return array + */ + public function provider_parse_language_header() + { + return array( + array( + 'en, en-us, en-gb, fr, fr-fr, es-es', + array( + 'en' => array( + '*' => (float) 1, + 'us' => (float) 1, + 'gb' => (float) 1 + ), + 'fr' => array( + '*' => (float) 1, + 'fr' => (float) 1 + ), + 'es' => array( + 'es' => (float) 1 + ) + ) + ), + array( + 'en; q=.9, en-us, en-gb, fr; q=.5, fr-fr; q=0.4, es-es; q=0.9, en-gb-gb; q=.45', + array( + 'en' => array( + '*' => 0.9, + 'us' => (float) 1, + 'gb' => (float) 1, + 'gb-gb' => 0.45 + ), + 'fr' => array( + '*' => 0.5, + 'fr' => 0.4 + ), + 'es' => array( + 'es' => 0.9 + ) + ) + ), + array( + NULL, + array( + '*' => array( + '*' => (float) 1 + ) + ) + ) + ); + } + + /** + * Tests the `parse_language_header` method parses the Accept-Language header + * correctly + * + * @dataProvider provider_parse_language_header + * + * @param string $accept accept + * @param array $expected expected + * @return void + */ + public function test_parse_language_header($accept, array $expected) + { + $this->assertSame($expected, HTTP_Header::parse_language_header($accept)); + } + + /** + * Data provider for test_create_cache_control + * + * @return array + */ + public function provider_create_cache_control() + { + return array( + array( + array( + 'public', + 'max-age' => 1800, + 'must-revalidate', + 's-max-age' => 3600 + ), + 'public, max-age=1800, must-revalidate, s-max-age=3600' + ), + array( + array( + 'max-age' => 1800, + 's-max-age' => 1800, + 'public', + 'must-revalidate', + ), + 'max-age=1800, s-max-age=1800, public, must-revalidate' + ), + array( + array( + 'private', + 'no-cache', + 'max-age' => 0, + 'must-revalidate' + ), + 'private, no-cache, max-age=0, must-revalidate' + ) + ); + } + + /** + * Tests that `create_cache_control()` outputs the correct cache control + * string from the supplied input + * + * @dataProvider provider_create_cache_control + * + * @param array $input input + * @param string $expected expected + * @return void + */ + public function test_create_cache_control(array $input, $expected) + { + $this->assertSame($expected, HTTP_Header::create_cache_control($input)); + } + + /** + * Data provider for parse_cache_control + * + * @return array + */ + public function provider_parse_cache_control() + { + return array( + array( + 'public, max-age=1800, must-revalidate, s-max-age=3600', + array( + 'public', + 'max-age' => 1800, + 'must-revalidate', + 's-max-age' => 3600 + ) + ), + array( + 'max-age=1800, s-max-age=1800, public, must-revalidate', + array( + 'max-age' => 1800, + 's-max-age' => 1800, + 'public', + 'must-revalidate', + ) + ), + array( + 'private, no-cache, max-age=0, must-revalidate', + array( + 'private', + 'no-cache', + 'max-age' => 0, + 'must-revalidate' + ) + ) + ); + } + + /** + * Tests that `parse_cache_control()` outputs the correct cache control + * parsed data from the input string + * + * @dataProvider provider_parse_cache_control + * + * @param string $input input + * @param array $expected expected + * @return void + */ + public function test_parse_cache_control($input, array $expected) + { + $parsed = HTTP_Header::parse_cache_control($input); + + $this->assertInternalType('array', $parsed); + + foreach ($expected as $key => $value) + { + if (is_int($key)) + { + $this->assertTrue(in_array($value, $parsed)); + } + else + { + $this->assertTrue(array_key_exists($key, $parsed)); + $this->assertSame($value, $parsed[$key]); + } + } + } + + /** + * Data provider for test_offsetSet + * + * @return array + */ + // @codingStandardsIgnoreStart + public function provider_offsetSet() + // @codingStandardsIgnoreEnd + { + return array( + array( + array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Accept' => 'text/html, text/plain; q=.1, */*', + 'Accept-Language' => 'en-gb, en-us, en; q=.1' + ), + array( + array( + 'Accept-Encoding', + 'compress, gzip', + FALSE + ) + ), + array( + 'content-type' => 'application/x-www-form-urlencoded', + 'accept' => 'text/html, text/plain; q=.1, */*', + 'accept-language' => 'en-gb, en-us, en; q=.1', + 'accept-encoding' => 'compress, gzip' + ) + ), + array( + array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Accept' => 'text/html, text/plain; q=.1, */*', + 'Accept-Language' => 'en-gb, en-us, en; q=.1' + ), + array( + array( + 'Accept-Encoding', + 'compress, gzip', + FALSE + ), + array( + 'Accept-Encoding', + 'bzip', + FALSE + ) + ), + array( + 'content-type' => 'application/x-www-form-urlencoded', + 'accept' => 'text/html, text/plain; q=.1, */*', + 'accept-language' => 'en-gb, en-us, en; q=.1', + 'accept-encoding' => array( + 'compress, gzip', + 'bzip' + ) + ) + ), + array( + array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Accept' => 'text/html, text/plain; q=.1, */*', + 'Accept-Language' => 'en-gb, en-us, en; q=.1' + ), + array( + array( + 'Accept-Encoding', + 'compress, gzip', + FALSE + ), + array( + 'Accept-Encoding', + 'bzip', + TRUE + ), + array( + 'Accept', + 'text/*', + FALSE + ) + ), + array( + 'content-type' => 'application/x-www-form-urlencoded', + 'accept' => array( + 'text/html, text/plain; q=.1, */*', + 'text/*' + ), + 'accept-language' => 'en-gb, en-us, en; q=.1', + 'accept-encoding' => 'bzip' + ) + ), + ); + } + + /** + * Ensures that offsetSet normalizes the array keys + * + * @dataProvider provider_offsetSet + * + * @param array $constructor constructor + * @param array $to_set to_set + * @param array $expected expected + * @return void + */ + // @codingStandardsIgnoreStart + public function test_offsetSet(array $constructor, array $to_set, array $expected) + // @codingStandardsIgnoreEnd + { + $http_header = new HTTP_Header($constructor); + + $reflection = new ReflectionClass($http_header); + $method = $reflection->getMethod('offsetSet'); + + foreach ($to_set as $args) + { + $method->invokeArgs($http_header, $args); + } + + $this->assertSame($expected, $http_header->getArrayCopy()); + } + + /** + * Data provider for test_offsetGet + * + * @return array + */ + // @codingStandardsIgnoreStart + public function provider_offsetGet() + // @codingStandardsIgnoreEnd + { + return array( + array( + array( + 'FoO' => 'bar', + 'START' => 'end', + 'true' => TRUE + ), + 'FOO', + 'bar' + ), + array( + array( + 'FoO' => 'bar', + 'START' => 'end', + 'true' => TRUE + ), + 'true', + TRUE + ), + array( + array( + 'FoO' => 'bar', + 'START' => 'end', + 'true' => TRUE + ), + 'True', + TRUE + ), + array( + array( + 'FoO' => 'bar', + 'START' => 'end', + 'true' => TRUE + ), + 'Start', + 'end' + ), + array( + array( + 'content-type' => 'bar', + 'Content-Type' => 'end', + 'Accept' => '*/*' + ), + 'content-type', + 'end' + ) + ); + } + + /** + * Ensures that offsetGet normalizes the array keys + * + * @dataProvider provider_offsetGet + * + * @param array start state + * @param string key to retrieve + * @param mixed expected + * @return void + */ + // @codingStandardsIgnoreStart + public function test_offsetGet(array $state, $key, $expected) + // @codingStandardsIgnoreEnd + { + $header = new HTTP_Header($state); + + $this->assertSame($expected, $header->offsetGet($key)); + } + + /** + * Data provider for test_offsetExists + * + * @return array + */ + // @codingStandardsIgnoreStart + public function provider_offsetExists() + // @codingStandardsIgnoreEnd + { + return array( + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'Content-Type', + TRUE + ), + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'CONTENT-TYPE', + TRUE + ), + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'accept-language', + TRUE + ), + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'x-powered-by', + FALSE + ) + ); + } + + /** + * Ensures that offsetExists normalizes the array key + * + * @dataProvider provider_offsetExists + * + * @param array $state state + * @param string $key key + * @param boolean $expected expected + * @return void + */ + // @codingStandardsIgnoreStart + public function test_offsetExists(array $state, $key, $expected) + // @codingStandardsIgnoreEnd + { + $header = new HTTP_Header($state); + + $this->assertSame($expected, $header->offsetExists($key)); + } + + /** + * Data provider for test_offsetUnset + * + * @return array + */ + // @codingStandardsIgnoreStart + public function provider_offsetUnset() + // @codingStandardsIgnoreEnd + { + return array( + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'Accept-Language', + array( + 'accept' => 'text/html, application/json', + 'content-type' => 'application/x-www-form-urlencoded' + ) + ), + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'ACCEPT', + array( + 'accept-language' => 'en, en-GB', + 'content-type' => 'application/x-www-form-urlencoded' + ) + ), + array( + array( + 'Accept' => 'text/html, application/json', + 'Accept-Language' => 'en, en-GB', + 'Content-Type' => 'application/x-www-form-urlencoded' + ), + 'content-type', + array( + 'accept' => 'text/html, application/json', + 'accept-language' => 'en, en-GB', + ) + ) + ); + } + + /** + * Tests that `offsetUnset` normalizes the key names properly + * + * @dataProvider provider_offsetUnset + * + * @param array $state state + * @param string $remove remove + * @param array $expected expected + * @return void + */ + // @codingStandardsIgnoreStart + public function test_offsetUnset(array $state, $remove, array $expected) + // @codingStandardsIgnoreEnd + { + $header = new HTTP_Header($state); + $header->offsetUnset($remove); + + $this->assertSame($expected, $header->getArrayCopy()); + } + + /** + * Provides data for test_parse_header_string + * + * @return array + */ + public function provider_parse_header_string() + { + return array( + array( + array( + "Content-Type: application/x-www-form-urlencoded\r\n", + "Accept: text/html, text/plain; q=.5, application/json, */* \r\n", + "X-Powered-By: Kohana Baby \r\n" + ), + array( + 'content-type' => 'application/x-www-form-urlencoded', + 'accept' => 'text/html, text/plain; q=.5, application/json, */* ', + 'x-powered-by' => 'Kohana Baby ' + ) + ), + array( + array( + "Content-Type: application/x-www-form-urlencoded\r\n", + "Accept: text/html, text/plain; q=.5, application/json, */* \r\n", + "X-Powered-By: Kohana Baby \r\n", + "Content-Type: application/json\r\n" + ), + array( + 'content-type' => array( + 'application/x-www-form-urlencoded', + 'application/json' + ), + 'accept' => 'text/html, text/plain; q=.5, application/json, */* ', + 'x-powered-by' => 'Kohana Baby ' + ) + ) + ); + } + + /** + * Tests that `parse_header_string` performs as expected + * + * @dataProvider provider_parse_header_string + * + * @param array headers + * @param array expected + * @return void + */ + public function test_parse_header_string(array $headers, array $expected) + { + $http_header = new HTTP_Header(array()); + + foreach ($headers as $header) + { + + $this->assertEquals(strlen($header), $http_header->parse_header_string(NULL, $header)); + } + + $this->assertSame($expected, $http_header->getArrayCopy()); + } + + /** + * Data Provider for test_accepts_at_quality + * + * @return array + */ + public function provider_accepts_at_quality() + { + return array( + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + 'application/json', + FALSE, + 1.0 + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + 'text/html', + FALSE, + 0.5 + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + 'text/plain', + FALSE, + 0.1 + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + 'text/plain', + TRUE, + FALSE + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + 'application/xml', + FALSE, + 1.0 + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + 'application/xml', + TRUE, + FALSE + ), + array( + array(), + 'application/xml', + FALSE, + 1.0 + ), + array( + array(), + 'application/xml', + TRUE, + FALSE + ) + ); + } + + /** + * Tests `accepts_at_quality` parsed the Accept: header as expected + * + * @dataProvider provider_accepts_at_quality + * + * @param array starting state + * @param string accept header to test + * @param boolean explicitly check + * @param mixed expected output + * @return void + */ + public function test_accepts_at_quality(array $state, $accept, $explicit, $expected) + { + $header = new HTTP_Header($state); + + $this->assertSame($expected, $header->accepts_at_quality($accept, $explicit)); + } + + /** + * Data provider for test_preferred_accept + * + * @return array + */ + public function provider_preferred_accept() + { + return array( + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + array( + 'text/html', + 'application/json', + 'text/plain' + ), + FALSE, + 'application/json' + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + array( + 'text/plain', + 'application/xml', + 'image/jpeg' + ), + FALSE, + 'application/xml' + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1' + ), + array( + 'text/plain', + 'application/xml', + 'image/jpeg' + ), + FALSE, + 'text/plain' + ), + array( + array( + 'Accept' => 'application/json, text/html; q=.5, text/*; q=.1, */*' + ), + array( + 'text/plain', + 'application/xml', + 'image/jpeg' + ), + TRUE, + FALSE + ), + + ); + } + + /** + * Tests `preferred_accept` returns the correct preferred type + * + * @dataProvider provider_preferred_accept + * + * @param array state + * @param array accepts + * @param string explicit + * @param string expected + * @return void + */ + public function test_preferred_accept(array $state, array $accepts, $explicit, $expected) + { + $header = new HTTP_Header($state); + + $this->assertSame($expected, $header->preferred_accept($accepts, $explicit)); + } + + /** + * Data provider for test_accepts_charset_at_quality + * + * @return array + */ + public function provider_accepts_charset_at_quality() + { + return array( + array( + array( + 'Accept-Charset' => 'utf-8, utf-10, utf-16, iso-8859-1' + ), + 'utf-8', + 1.0 + ), + array( + array( + 'Accept-Charset' => 'utf-8, utf-10, utf-16, iso-8859-1' + ), + 'utf-16', + 1.0 + ), + array( + array( + 'Accept-Charset' => 'utf-8; q=.1, utf-10, utf-16; q=.2, iso-8859-1' + ), + 'utf-8', + 0.1 + ), + array( + array( + 'Accept-Charset' => 'utf-8; q=.1, utf-10, utf-16; q=.2, iso-8859-1; q=.5' + ), + 'iso-8859-1', + 0.5 + ) + ); + } + + /** + * Tests `accepts_charset_at_quality` works as expected, returning the correct + * quality value + * + * @dataProvider provider_accepts_charset_at_quality + * + * @param array state + * @param string charset + * @param string expected + * @return void + */ + public function test_accepts_charset_at_quality(array $state, $charset, $expected) + { + $header = new HTTP_Header($state); + + $this->assertSame($expected, $header->accepts_charset_at_quality($charset)); + } + + /** + * Data provider for test_preferred_charset + * + * @return array + */ + public function provider_preferred_charset() + { + return array( + array( + array( + 'Accept-Charset' => 'utf-8, utf-10, utf-16, iso-8859-1' + ), + array( + 'utf-8', + 'utf-16' + ), + 'utf-8' + ), + array( + array( + 'Accept-Charset' => 'utf-8, utf-10, utf-16, iso-8859-1' + ), + array( + 'UTF-10' + ), + 'UTF-10' + ), + ); + } + + /** + * Tests `preferred_charset` works as expected, returning the correct charset + * from the list supplied + * + * @dataProvider provider_preferred_charset + * + * @param array state + * @param array charsets + * @param string expected + * @return void + */ + public function test_preferred_charset(array $state, array $charsets, $expected) + { + $header = new HTTP_Header($state); + + $this->assertSame($expected, $header->preferred_charset($charsets)); + } + + /** + * Data provider for test_accepts_encoding_at_quality + * + * @return array + */ + public function provider_accepts_encoding_at_quality() + { + return array( + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + 'gzip', + FALSE, + 1.0 + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + 'gzip', + TRUE, + 1.0 + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + 'blowfish', + FALSE, + 0.7 + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + 'bzip', + FALSE, + 0.5 + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + 'bzip', + TRUE, + (float) 0 + ) + ); + } + + /** + * Tests `accepts_encoding_at_quality` parses and returns the correct + * quality value for Accept-Encoding headers + * + * @dataProvider provider_accepts_encoding_at_quality + * + * @param array state + * @param string encoding + * @param boolean explicit + * @param float expected + * @return void + */ + public function test_accepts_encoding_at_quality(array $state, $encoding, $explicit, $expected) + { + $header = new HTTP_Header($state); + $this->assertSame($expected, $header->accepts_encoding_at_quality($encoding, $explicit)); + } + + /** + * Data provider for test_preferred_encoding + * + * @return array + */ + public function provider_preferred_encoding() + { + return array( + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + array('gzip', 'blowfish', 'bzip'), + FALSE, + 'gzip' + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + array('bzip', 'ROT-13'), + FALSE, + 'bzip' + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.7, *; q=.5' + ), + array('bzip', 'ROT-13'), + TRUE, + FALSE + ), + array( + array( + 'accept-encoding' => 'compress, gzip, blowfish; q=.2, *; q=.5' + ), + array('ROT-13', 'blowfish'), + FALSE, + 'ROT-13' + ), + ); + } + + /** + * Tests that `preferred_encoding` parses and returns the correct + * encoding type + * + * @dataProvider provider_preferred_encoding + * + * @param array state in + * @param array encodings to interrogate + * @param boolean explicit check + * @param string expected output + * @return void + */ + public function test_preferred_encoding(array $state, array $encodings, $explicit, $expected) + { + $header = new HTTP_Header($state); + $this->assertSame($expected, $header->preferred_encoding($encodings, $explicit)); + } + + /** + * Data provider for test_accepts_language_at_quality + * + * @return array + */ + public function provider_accepts_language_at_quality() + { + return array( + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + 'en', + FALSE, + 0.5 + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + 'en-gb', + FALSE, + 0.7 + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + 'en', + TRUE, + 0.5 + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + 'fr-ni', + FALSE, + 0.8 + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + 'fr-ni', + TRUE, + (float) 0 + ), + array( + array( + 'accept-language' => 'en-US' + ), + 'en-us', + TRUE, + (float) 1 + ), + ); + } + + /** + * Tests `accepts_language_at_quality` parses the Accept-Language header + * correctly and identifies the correct quality supplied, explicit or not + * + * @dataProvider provider_accepts_language_at_quality + * + * @param array state in + * @param string language to interrogate + * @param boolean explicit check + * @param float expected output + * @return void + */ + public function test_accepts_language_at_quality(array $state, $language, $explicit, $expected) + { + $header = new HTTP_Header($state); + $this->assertSame($expected, $header->accepts_language_at_quality($language, $explicit)); + } + + /** + * Data provider for test_preferred_language + * + * @return array + */ + public function provider_preferred_language() + { + return array( + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + array( + 'en', + 'fr', + 'en-gb' + ), + FALSE, + 'fr' + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + array( + 'en', + 'fr', + 'en-gb' + ), + TRUE, + 'fr' + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + array( + 'en-au', + 'fr-ni', + 'fr' + ), + FALSE, + 'fr-ni' + ), + array( + array( + 'accept-language' => 'en-us; q=.9, en-gb; q=.7, en; q=.5, fr-fr; q=.9, fr; q=.8' + ), + array( + 'en-au', + 'fr-ni', + 'fr' + ), + TRUE, + 'fr' + ), + array( + array( + 'accept-language' => 'en-US' + ), + array( + 'en-us' + ), + TRUE, + 'en-us' + ), + ); + } + + /** + * Tests that `preferred_language` correctly identifies the right + * language based on the Accept-Language header and `$explicit` setting + * + * @dataProvider provider_preferred_language + * + * @param array state in + * @param array languages to interrogate + * @param boolean explicit check + * @param string expected output + * @return void + */ + public function test_preferred_language(array $state, array $languages, $explicit, $expected) + { + $header = new HTTP_Header($state); + $this->assertSame($expected, $header->preferred_language($languages, $explicit)); + } + + /** + * Data provider for test_send_headers + * + * @return array + */ + public function provider_send_headers() + { + $content_type = Kohana::$content_type.'; charset='.Kohana::$charset; + + return array( + array( + array(), + array( + 'HTTP/1.1 200 OK', + 'Content-Type: '.$content_type, + ), + FALSE, + ), + array( + array(), + array( + 'HTTP/1.1 200 OK', + 'Content-Type: '.$content_type, + 'X-Powered-By: '.Kohana::version(), + ), + TRUE, + ), + array( + array( + 'accept' => 'text/html, text/plain, text/*, */*', + 'accept-charset' => 'utf-8, utf-10, iso-8859-1', + 'accept-encoding' => 'compress, gzip', + 'accept-language' => 'en, en-gb, en-us' + ), + array( + 'HTTP/1.1 200 OK', + 'Accept: text/html, text/plain, text/*, */*', + 'Accept-Charset: utf-8, utf-10, iso-8859-1', + 'Accept-Encoding: compress, gzip', + 'Accept-Language: en, en-gb, en-us', + 'Content-Type: '.$content_type, + ), + FALSE + ), + array( + array( + 'accept' => 'text/html, text/plain, text/*, */*', + 'accept-charset' => 'utf-8, utf-10, iso-8859-1', + 'accept-encoding' => 'compress, gzip', + 'accept-language' => 'en, en-gb, en-us', + 'content-type' => 'application/json', + 'x-powered-by' => 'Mohana', + 'x-ssl-enabled' => 'TRUE' + ), + array( + 'HTTP/1.1 200 OK', + 'Accept: text/html, text/plain, text/*, */*', + 'Accept-Charset: utf-8, utf-10, iso-8859-1', + 'Accept-Encoding: compress, gzip', + 'Accept-Language: en, en-gb, en-us', + 'Content-Type: application/json', + 'X-Powered-By: Mohana', + 'X-Ssl-Enabled: TRUE' + ), + TRUE + ) + ); + } + + /** + * Tests that send headers processes the headers sent to PHP correctly + * + * @dataProvider provider_send_headers + * + * @param array state in + * @param array expected out + * @return void + */ + public function test_send_headers(array $state, array $expected, $expose) + { + Kohana::$expose = $expose; + + $response = new Response; + $response->headers($state); + + $this->assertSame( + $expected, + $response->send_headers(FALSE, array($this, 'send_headers_handler')) + ); + } + + /** + * Callback handler for send headers + * + * @param array headers + * @param boolean replace + * @return array + */ + public function send_headers_handler($response, $headers, $replace) + { + return $headers; + } +} // End Kohana_HTTP_HeaderTest \ No newline at end of file diff --git a/system/tests/kohana/I18nTest.php b/system/tests/kohana/I18nTest.php new file mode 100644 index 0000000..b91fdd4 --- /dev/null +++ b/system/tests/kohana/I18nTest.php @@ -0,0 +1,90 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_I18nTest extends Unittest_TestCase { + + /** + * Default values for the environment, see setEnvironment + * @var array + */ + // @codingStandardsIgnoreStart + protected $environmentDefault = array( + 'I18n::$lang' => 'en-us', + ); + // @codingStandardsIgnoreEnd + + /** + * Provides test data for test_lang() + * + * @return array + */ + public function provider_lang() + { + return array( + // $input, $expected_result + array(NULL, 'en-us'), + array('es-es', 'es-es'), + ); + } + + /** + * Tests I18n::lang() + * + * @test + * @dataProvider provider_lang + * @param boolean $input Input for I18n::lang + * @param boolean $expected Output for I18n::lang + */ + public function test_lang($input, $expected_result) + { + $this->assertSame($expected_result, I18n::lang($input)); + $this->assertSame($expected_result, I18n::lang()); + } + + /** + * Provides test data for test_get() + * + * @return array + */ + public function provider_get() + { + return array( + // $value, $result + array('en-us', 'Hello, world!', 'Hello, world!'), + array('es-es', 'Hello, world!', '¡Hola, mundo!'), + array('fr-fr', 'Hello, world!', 'Bonjour, monde!'), + ); + } + + /** + * Tests i18n::get() + * + * @test + * @dataProvider provider_get + * @param boolean $input Input for File::mime + * @param boolean $expected Output for File::mime + */ + public function test_get($lang, $input, $expected) + { + I18n::lang($lang); + $this->assertSame($expected, I18n::get($input)); + + // Test immediate translation, issue #3085 + I18n::lang('en-us'); + $this->assertSame($expected, I18n::get($input, $lang)); + } + +} diff --git a/system/tests/kohana/InflectorTest.php b/system/tests/kohana/InflectorTest.php new file mode 100644 index 0000000..9478a0d --- /dev/null +++ b/system/tests/kohana/InflectorTest.php @@ -0,0 +1,186 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_InflectorTest extends Unittest_TestCase +{ + /** + * Provides test data for test_lang() + * + * @return array + */ + public function provider_uncountable() + { + return array( + // $value, $result + array('fish', TRUE), + array('cat', FALSE), + array('deer', TRUE), + array('bison', TRUE), + array('friend', FALSE), + ); + } + + /** + * Tests Inflector::uncountable + * + * @test + * @dataProvider provider_uncountable + * @param boolean $input Input for File::mime + * @param boolean $expected Output for File::mime + */ + public function test_uncountable($input, $expected) + { + $this->assertSame($expected, Inflector::uncountable($input)); + } + + /** + * Provides test data for test_lang() + * + * @return array + */ + public function provider_singular() + { + return array( + // $value, $result + array('fish', NULL, 'fish'), + array('cats', NULL, 'cat'), + array('cats', 2, 'cats'), + array('cats', '2', 'cats'), + array('children', NULL, 'child'), + array('meters', 0.6, 'meters'), + array('meters', 1.6, 'meters'), + array('meters', 1.0, 'meter'), + array('status', NULL, 'status'), + array('statuses', NULL, 'status'), + array('heroes', NULL, 'hero'), + ); + } + + /** + * Tests Inflector::singular + * + * @test + * @dataProvider provider_singular + * @param boolean $input Input for File::mime + * @param boolean $expected Output for File::mime + */ + public function test_singular($input, $count, $expected) + { + $this->assertSame($expected, Inflector::singular($input, $count)); + } + + /** + * Provides test data for test_lang() + * + * @return array + */ + public function provider_plural() + { + return array( + // $value, $result + array('fish', NULL, 'fish'), + array('cat', NULL, 'cats'), + array('cats', 1, 'cats'), + array('cats', '1', 'cats'), + array('movie', NULL, 'movies'), + array('meter', 0.6, 'meters'), + array('meter', 1.6, 'meters'), + array('meter', 1.0, 'meter'), + array('hero', NULL, 'heroes'), + array('Dog', NULL, 'Dogs'), // Titlecase + array('DOG', NULL, 'DOGS'), // Uppercase + ); + } + + /** + * Tests Inflector::plural + * + * @test + * @dataProvider provider_plural + * @param boolean $input Input for File::mime + * @param boolean $expected Output for File::mime + */ + public function test_plural($input, $count, $expected) + { + $this->assertSame($expected, Inflector::plural($input, $count)); + } + + /** + * Provides test data for test_camelize() + * + * @return array + */ + public function provider_camelize() + { + return array( + // $value, $result + array('mother cat', 'camelize', 'motherCat'), + array('kittens in bed', 'camelize', 'kittensInBed'), + array('mother cat', 'underscore', 'mother_cat'), + array('kittens in bed', 'underscore', 'kittens_in_bed'), + array('kittens-are-cats', 'humanize', 'kittens are cats'), + array('dogs_as_well', 'humanize', 'dogs as well'), + ); + } + + /** + * Tests Inflector::camelize + * + * @test + * @dataProvider provider_camelize + * @param boolean $input Input for File::mime + * @param boolean $expected Output for File::mime + */ + public function test_camelize($input, $method, $expected) + { + $this->assertSame($expected, Inflector::$method($input)); + } + + /** + * Provides data for test_decamelize() + * + * @return array + */ + public function provider_decamelize() + { + return array( + array('getText', '_', 'get_text'), + array('getJSON', '_', 'get_json'), + array('getLongText', '_', 'get_long_text'), + array('getI18N', '_', 'get_i18n'), + array('getL10n', '_', 'get_l10n'), + array('getTe5t1ng', '_', 'get_te5t1ng'), + array('OpenFile', '_', 'open_file'), + array('CloseIoSocket', '_', 'close_io_socket'), + array('fooBar', ' ', 'foo bar'), + array('camelCase', '+', 'camel+case'), + ); + } + + /** + * Tests Inflector::decamelize() + * + * @test + * @dataProvider provider_decamelize + * @param string $input Camelized string + * @param string $glue Glue + * @param string $expected Expected string + */ + public function test_decamelize($input, $glue, $expected) + { + $this->assertSame($expected, Inflector::decamelize($input, $glue)); + } +} diff --git a/system/tests/kohana/LogTest.php b/system/tests/kohana/LogTest.php new file mode 100644 index 0000000..df77c39 --- /dev/null +++ b/system/tests/kohana/LogTest.php @@ -0,0 +1,111 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_LogTest extends Unittest_TestCase +{ + + /** + * Tests that when a new logger is created the list of messages is initially + * empty + * + * @test + * @covers Log + */ + public function test_messages_is_initially_empty() + { + $logger = new Log; + + $this->assertAttributeSame(array(), '_messages', $logger); + } + + /** + * Tests that when a new logger is created the list of writers is initially + * empty + * + * @test + * @covers Log + */ + public function test_writers_is_initially_empty() + { + $logger = new Log; + + $this->assertAttributeSame(array(), '_writers', $logger); + } + + /** + * Test that attaching a log writer using an array of levels adds it to the array of log writers + * + * @TODO Is this test too specific? + * + * @test + * @covers Log::attach + */ + public function test_attach_attaches_log_writer_and_returns_this() + { + $logger = new Log; + $writer = $this->getMockForAbstractClass('Log_Writer'); + + $this->assertSame($logger, $logger->attach($writer)); + + $this->assertAttributeSame( + array(spl_object_hash($writer) => array('object' => $writer, 'levels' => array())), + '_writers', + $logger + ); + } + + /** + * Test that attaching a log writer using a min/max level adds it to the array of log writers + * + * @TODO Is this test too specific? + * + * @test + * @covers Log::attach + */ + public function test_attach_attaches_log_writer_min_max_and_returns_this() + { + $logger = new Log; + $writer = $this->getMockForAbstractClass('Log_Writer'); + + $this->assertSame($logger, $logger->attach($writer, Log::NOTICE, Log::CRITICAL)); + + $this->assertAttributeSame( + array(spl_object_hash($writer) => array('object' => $writer, 'levels' => array(Log::CRITICAL, Log::ERROR, Log::WARNING, Log::NOTICE))), + '_writers', + $logger + ); + } + + /** + * When we call detach() we expect the specified log writer to be removed + * + * @test + * @covers Log::detach + */ + public function test_detach_removes_log_writer_and_returns_this() + { + $logger = new Log; + $writer = $this->getMockForAbstractClass('Log_Writer'); + + $logger->attach($writer); + + $this->assertSame($logger, $logger->detach($writer)); + + $this->assertAttributeSame(array(), '_writers', $logger); + } + + +} diff --git a/system/tests/kohana/ModelTest.php b/system/tests/kohana/ModelTest.php new file mode 100644 index 0000000..8e33d84 --- /dev/null +++ b/system/tests/kohana/ModelTest.php @@ -0,0 +1,36 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_ModelTest extends Unittest_TestCase +{ + /** + * Test the model's factory. + * + * @test + * @covers Model::factory + */ + public function test_create() + { + $foobar = Model::factory('Foobar'); + + $this->assertEquals(TRUE, $foobar instanceof Model); + } +} + +class Model_Foobar extends Model +{ + +} diff --git a/system/tests/kohana/NumTest.php b/system/tests/kohana/NumTest.php new file mode 100644 index 0000000..ddb9bac --- /dev/null +++ b/system/tests/kohana/NumTest.php @@ -0,0 +1,205 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_NumTest extends Unittest_TestCase +{ + protected $default_locale; + + /** + * SetUp test enviroment + */ + // @codingStandardsIgnoreStart + public function setUp() + // @codingStandardsIgnoreEnd + { + parent::setUp(); + + setlocale(LC_ALL, 'en_US.utf8'); + } + + /** + * Tear down environment + */ + // @codingStandardsIgnoreStart + public function tearDown() + // @codingStandardsIgnoreEnd + { + parent::tearDown(); + + setlocale(LC_ALL, $this->default_locale); + } + + /** + * Provides test data for test_bytes() + * + * @return array + */ + public function provider_bytes() + { + return array( + array(204800.0, '200K'), + array(5242880.0, '5MiB'), + array(1000.0, 1000), + array(2684354560.0, '2.5GB'), + ); + } + + /** + * Tests Num::bytes() + * + * @test + * @covers Num::bytes + * @dataProvider provider_bytes + * @param integer Expected Value + * @param string Input value + */ + public function test_bytes($expected, $size) + { + $this->assertSame($expected, Num::bytes($size)); + } + + /** + * Provides test data for test_ordinal() + * @return array + */ + public function provider_ordinal() + { + return array( + array(0, 'th'), + array(1, 'st'), + array(21, 'st'), + array(112, 'th'), + array(23, 'rd'), + array(42, 'nd'), + ); + } + + /** + * + * @test + * @dataProvider provider_ordinal + * @param integer $number + * @param $expected + */ + public function test_ordinal($number, $expected) + { + $this->assertSame($expected, Num::ordinal($number)); + } + + /** + * Provides test data for test_format() + * @return array + */ + public function provider_format() + { + return array( + // English + array(10000, 2, FALSE, '10,000.00'), + array(10000, 2, TRUE, '10,000.00'), + + // Additional dp's should be removed + array(123.456, 2, FALSE, '123.46'), + array(123.456, 2, TRUE, '123.46'), + ); + } + + /** + * @todo test locales + * @test + * @dataProvider provider_format + * @param integer $number + * @param integer $places + * @param boolean $monetary + * @param string $expected + */ + public function test_format($number, $places, $monetary, $expected) + { + $this->assertSame($expected, Num::format($number, $places, $monetary)); + } + + /** + * Provides data for test_round() + * @return array + */ + function provider_round() + { + return array( + array(5.5, 0, array( + 6.0, + 5.0, + 6.0, + 5.0, + )), + array(42.5, 0, array( + 43.0, + 42.0, + 42.0, + 43.0, + )), + array(10.4, 0, array( + 10.0, + 10.0, + 10.0, + 10.0, + )), + array(10.8, 0, array( + 11.0, + 11.0, + 11.0, + 11.0, + )), + array(-5.5, 0, array( + -6.0, + -5.0, + -6.0, + -5.0, + )), + array(-10.5, 0, array( + -11.0, + -10.0, + -10.0, + -11.0, + )), + array(26.12375, 4, array( + 26.1238, + 26.1237, + 26.1238, + 26.1237, + )), + array(26.12325, 4, array( + 26.1233, + 26.1232, + 26.1232, + 26.1233, + )), + ); + } + + /** + * @test + * @dataProvider provider_round + * @param number $input + * @param integer $precision + * @param integer $mode + * @param number $expected + */ + function test_round($input, $precision, $expected) + { + foreach (array(Num::ROUND_HALF_UP, Num::ROUND_HALF_DOWN, Num::ROUND_HALF_EVEN, Num::ROUND_HALF_ODD) as $i => $mode) + { + $this->assertSame($expected[$i], Num::round($input, $precision, $mode, FALSE)); + } + } +} diff --git a/system/tests/kohana/RequestTest.php b/system/tests/kohana/RequestTest.php new file mode 100644 index 0000000..3ba516f --- /dev/null +++ b/system/tests/kohana/RequestTest.php @@ -0,0 +1,720 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_RequestTest extends Unittest_TestCase +{ + protected $_inital_request; + + // @codingStandardsIgnoreStart + public function setUp() + // @codingStandardsIgnoreEnd + { + parent::setUp(); + $this->_initial_request = Request::$initial; + Request::$initial = new Request('/'); + } + + // @codingStandardsIgnoreStart + public function tearDown() + // @codingStandardsIgnoreEnd + { + Request::$initial = $this->_initial_request; + parent::tearDown(); + } + + public function test_initial() + { + $this->setEnvironment(array( + 'Request::$initial' => NULL, + 'Request::$client_ip' => NULL, + 'Request::$user_agent' => NULL, + '_SERVER' => array( + 'HTTPS' => NULL, + 'PATH_INFO' => '/', + 'HTTP_REFERER' => 'http://example.com/', + 'HTTP_USER_AGENT' => 'whatever (Mozilla 5.0/compatible)', + 'REMOTE_ADDR' => '127.0.0.1', + 'REQUEST_METHOD' => 'GET', + 'HTTP_X_REQUESTED_WITH' => 'ajax-or-something', + ), + '_GET' => array(), + '_POST' => array(), + )); + + $request = Request::factory(); + + $this->assertEquals(Request::$initial, $request); + + $this->assertEquals(Request::$client_ip, '127.0.0.1'); + + $this->assertEquals(Request::$user_agent, 'whatever (Mozilla 5.0/compatible)'); + + $this->assertEquals($request->protocol(), 'HTTP/1.1'); + + $this->assertEquals($request->referrer(), 'http://example.com/'); + + $this->assertEquals($request->requested_with(), 'ajax-or-something'); + + $this->assertEquals($request->query(), array()); + + $this->assertEquals($request->post(), array()); + } + + /** + * Tests that the allow_external flag prevents an external request. + * + * @return null + */ + public function test_disable_external_tests() + { + $this->setEnvironment( + array( + 'Request::$initial' => NULL, + ) + ); + + $request = new Request('http://www.google.com/', array(), FALSE); + + $this->assertEquals(FALSE, $request->is_external()); + } + + /** + * Provides the data for test_create() + * @return array + */ + public function provider_create() + { + return array( + array('foo/bar', 'Request_Client_Internal'), + array('http://google.com', 'Request_Client_External'), + ); + } + + /** + * Ensures the create class is created with the correct client + * + * @test + * @dataProvider provider_create + */ + public function test_create($uri, $client_class) + { + $request = Request::factory($uri); + + $this->assertInstanceOf($client_class, $request->client()); + } + + /** + * Ensure that parameters can be read + * + * @test + */ + public function test_param() + { + $route = new Route('((/(/)))'); + + $uri = 'foo/bar/id'; + $request = Request::factory($uri, NULL, TRUE, array($route)); + + // We need to execute the request before it has matched a route + try + { + $request->execute(); + } + catch (Exception $e) {} + + $this->assertArrayHasKey('id', $request->param()); + $this->assertArrayNotHasKey('foo', $request->param()); + $this->assertEquals($request->uri(), $uri); + + // Ensure the params do not contain contamination from controller, action, route, uri etc etc + $params = $request->param(); + + // Test for illegal components + $this->assertArrayNotHasKey('controller', $params); + $this->assertArrayNotHasKey('action', $params); + $this->assertArrayNotHasKey('directory', $params); + $this->assertArrayNotHasKey('uri', $params); + $this->assertArrayNotHasKey('route', $params); + + $route = new Route('()', array('uri' => '.+')); + $route->defaults(array('controller' => 'foobar', 'action' => 'index')); + $request = Request::factory('foobar', NULL, TRUE, array($route)); + + // We need to execute the request before it has matched a route + try + { + $request->execute(); + } + catch (Exception $e) {} + + $this->assertSame('foobar', $request->param('uri')); + } + + /** + * Tests Request::method() + * + * @test + */ + public function test_method() + { + $request = Request::factory('foo/bar'); + + $this->assertEquals($request->method(), 'GET'); + $this->assertEquals(($request->method('post') === $request), TRUE); + $this->assertEquals(($request->method() === 'POST'), TRUE); + } + + /** + * Tests Request::route() + * + * @test + */ + public function test_route() + { + $request = Request::factory(''); // This should always match something, no matter what changes people make + + // We need to execute the request before it has matched a route + try + { + $request->execute(); + } + catch (Exception $e) {} + + $this->assertInstanceOf('Route', $request->route()); + } + + /** + * Tests Request::route() + * + * @test + */ + public function test_route_is_not_set_before_execute() + { + $request = Request::factory(''); // This should always match something, no matter what changes people make + + // The route should be NULL since the request has not been executed yet + $this->assertEquals($request->route(), NULL); + } + + /** + * Tests Request::accept_type() + * + * @test + * @covers Request::accept_type + */ + public function test_accept_type() + { + $this->assertEquals(array('*/*' => 1), Request::accept_type()); + } + + /** + * Provides test data for Request::accept_lang() + * @return array + */ + public function provider_accept_lang() + { + return array( + array('en-us', 1, array('_SERVER' => array('HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5'))), + array('en-us', 1, array('_SERVER' => array('HTTP_ACCEPT_LANGUAGE' => 'en-gb'))), + array('en-us', 1, array('_SERVER' => array('HTTP_ACCEPT_LANGUAGE' => 'sp-sp;q=0.5'))) + ); + } + + /** + * Tests Request::accept_lang() + * + * @test + * @covers Request::accept_lang + * @dataProvider provider_accept_lang + * @param array $params Query string + * @param string $expected Expected result + * @param array $enviroment Set environment + */ + public function test_accept_lang($params, $expected, $enviroment) + { + $this->setEnvironment($enviroment); + + $this->assertEquals( + $expected, + Request::accept_lang($params) + ); + } + + /** + * Provides test data for Request::url() + * @return array + */ + public function provider_url() + { + return array( + array( + 'foo/bar', + 'http', + 'http://localhost/kohana/foo/bar' + ), + array( + 'foo', + 'http', + 'http://localhost/kohana/foo' + ), + ); + } + + /** + * Tests Request::url() + * + * @test + * @dataProvider provider_url + * @covers Request::url + * @param string $uri the uri to use + * @param string $protocol the protocol to use + * @param array $expected The string we expect + */ + public function test_url($uri, $protocol, $expected) + { + if ( ! isset($_SERVER['argc'])) + { + $_SERVER['argc'] = 1; + } + + $this->setEnvironment(array( + 'Kohana::$base_url' => '/kohana/', + '_SERVER' => array('HTTP_HOST' => 'localhost', 'argc' => $_SERVER['argc']), + 'Kohana::$index_file' => FALSE, + )); + + $this->assertEquals(Request::factory($uri)->url($protocol), $expected); + } + + /** + * Data provider for test_set_protocol() test + * + * @return array + */ + public function provider_set_protocol() + { + return array( + array( + 'http/1.1', + 'HTTP/1.1', + ), + array( + 'ftp', + 'FTP', + ), + array( + 'hTTp/1.0', + 'HTTP/1.0', + ), + ); + } + + /** + * Tests the protocol() method + * + * @dataProvider provider_set_protocol + * + * @return null + */ + public function test_set_protocol($protocol, $expected) + { + $request = Request::factory(); + + // Set the supplied protocol + $result = $request->protocol($protocol); + + // Test the set value + $this->assertSame($expected, $request->protocol()); + + // Test the return value + $this->assertTrue($request instanceof $result); + } + + /** + * Provides data for test_post_max_size_exceeded() + * + * @return array + */ + public function provider_post_max_size_exceeded() + { + // Get the post max size + $post_max_size = Num::bytes(ini_get('post_max_size')); + + return array( + array( + $post_max_size+200000, + TRUE + ), + array( + $post_max_size-20, + FALSE + ), + array( + $post_max_size, + FALSE + ) + ); + } + + /** + * Tests the post_max_size_exceeded() method + * + * @dataProvider provider_post_max_size_exceeded + * + * @param int content_length + * @param bool expected + * @return void + */ + public function test_post_max_size_exceeded($content_length, $expected) + { + // Ensure the request method is set to POST + Request::$initial->method(HTTP_Request::POST); + + // Set the content length + $_SERVER['CONTENT_LENGTH'] = $content_length; + + // Test the post_max_size_exceeded() method + $this->assertSame(Request::post_max_size_exceeded(), $expected); + } + + /** + * Provides data for test_uri_only_trimed_on_internal() + * + * @return array + */ + public function provider_uri_only_trimed_on_internal() + { + $old_request = Request::$initial; + Request::$initial = new Request(TRUE); + + $result = array( + array( + new Request('http://www.google.com'), + 'http://www.google.com' + ), + array( + new Request('http://www.google.com/'), + 'http://www.google.com/' + ), + array( + new Request('foo/bar/'), + 'foo/bar' + ), + array( + new Request('foo/bar'), + 'foo/bar' + ), + array( + new Request('/'), + '/' + ), + array( + new Request(''), + '/' + ) + ); + + Request::$initial = $old_request; + return $result; + } + + /** + * Tests that the uri supplied to Request is only trimed + * for internal requests. + * + * @dataProvider provider_uri_only_trimed_on_internal + * + * @return void + */ + public function test_uri_only_trimed_on_internal(Request $request, $expected) + { + $this->assertSame($request->uri(), $expected); + } + + /** + * Data provider for test_options_set_to_external_client() + * + * @return array + */ + public function provider_options_set_to_external_client() + { + $provider = array( + array( + array( + CURLOPT_PROXYPORT => 8080, + CURLOPT_PROXYTYPE => CURLPROXY_HTTP, + CURLOPT_VERBOSE => TRUE + ), + array( + CURLOPT_PROXYPORT => 8080, + CURLOPT_PROXYTYPE => CURLPROXY_HTTP, + CURLOPT_VERBOSE => TRUE + ) + ) + ); + + return $provider; + } + + /** + * Test for Request_Client_External::options() to ensure options + * can be set to the external client (for cURL and PECL_HTTP) + * + * @dataProvider provider_options_set_to_external_client + * + * @param array settings + * @param array expected + * @return void + */ + public function test_options_set_to_external_client($settings, $expected) + { + $request_client = Request_Client_External::factory(array(), 'Request_Client_Curl'); + + // Test for empty array + $this->assertSame(array(), $request_client->options()); + + // Test that set works as expected + $this->assertSame($request_client->options($settings), $request_client); + + // Test that each setting is present and returned + foreach ($expected as $key => $value) + { + $this->assertSame($request_client->options($key), $value); + } + } + + /** + * Provides data for test_headers_get() + * + * @return array + */ + public function provider_headers_get() + { + $x_powered_by = 'Kohana Unit Test'; + $content_type = 'application/x-www-form-urlencoded'; + + return array( + array( + $request = Request::factory('foo/bar') + ->headers(array( + 'x-powered-by' => $x_powered_by, + 'content-type' => $content_type + ) + ), + array( + 'x-powered-by' => $x_powered_by, + 'content-type' => $content_type + ) + ) + ); + } + + /** + * Tests getting headers from the Request object + * + * @dataProvider provider_headers_get + * + * @param Request request to test + * @param array headers to test against + * @return void + */ + public function test_headers_get($request, $headers) + { + foreach ($headers as $key => $expected_value) + { + $this->assertSame( (string) $request->headers($key), $expected_value); + } + } + + /** + * Provides data for test_headers_set + * + * @return array + */ + public function provider_headers_set() + { + return array( + array( + Request::factory(), + array( + 'content-type' => 'application/x-www-form-urlencoded', + 'x-test-header' => 'foo' + ), + "Content-Type: application/x-www-form-urlencoded\r\nX-Test-Header: foo\r\n\r\n" + ), + array( + Request::factory(), + array( + 'content-type' => 'application/json', + 'x-powered-by' => 'kohana' + ), + "Content-Type: application/json\r\nX-Powered-By: kohana\r\n\r\n" + ) + ); + } + + /** + * Tests the setting of headers to the request object + * + * @dataProvider provider_headers_set + * + * @param Request request object + * @param array header(s) to set to the request object + * @param string expected http header + * @return void + */ + public function test_headers_set(Request $request, $headers, $expected) + { + $request->headers($headers); + $this->assertSame($expected, (string) $request->headers()); + } + + /** + * Provides test data for test_query_parameter_parsing() + * + * @return array + */ + public function provider_query_parameter_parsing() + { + return array( + array( + new Request('foo/bar'), + array( + 'foo' => 'bar', + 'sna' => 'fu' + ), + array( + 'foo' => 'bar', + 'sna' => 'fu' + ), + ), + array( + new Request('foo/bar?john=wayne&peggy=sue'), + array( + 'foo' => 'bar', + 'sna' => 'fu' + ), + array( + 'john' => 'wayne', + 'peggy' => 'sue', + 'foo' => 'bar', + 'sna' => 'fu' + ), + ), + array( + new Request('http://host.tld/foo/bar?john=wayne&peggy=sue'), + array( + 'foo' => 'bar', + 'sna' => 'fu' + ), + array( + 'john' => 'wayne', + 'peggy' => 'sue', + 'foo' => 'bar', + 'sna' => 'fu' + ), + ), + ); + } + + /** + * Tests that query parameters are parsed correctly + * + * @dataProvider provider_query_parameter_parsing + * + * @param Request request + * @param array query + * @param array expected + * @return void + */ + public function test_query_parameter_parsing(Request $request, $query, $expected) + { + foreach ($query as $key => $value) + { + $request->query($key, $value); + } + + $this->assertSame($expected, $request->query()); + } + + /** + * Provides data for test_client + * + * @return array + */ + public function provider_client() + { + $internal_client = new Request_Client_Internal; + $external_client = new Request_Client_Stream; + + return array( + array( + new Request('http://kohanaframework.org'), + $internal_client, + $internal_client + ), + array( + new Request('foo/bar'), + $external_client, + $external_client + ) + ); + } + + /** + * Tests the getter/setter for request client + * + * @dataProvider provider_client + * + * @param Request $request + * @param Request_Client $client + * @param Request_Client $expected + * @return void + */ + public function test_client(Request $request, Request_Client $client, Request_Client $expected) + { + $request->client($client); + $this->assertSame($expected, $request->client()); + } + + /** + * Tests that the Request constructor passes client params on to the + * Request_Client once created. + */ + public function test_passes_client_params() + { + $request = Request::factory('http://example.com/', array( + 'follow' => TRUE, + 'strict_redirect' => FALSE + )); + + $client = $request->client(); + + $this->assertEquals($client->follow(), TRUE); + $this->assertEquals($client->strict_redirect(), FALSE); + } + + + +} // End Kohana_RequestTest + +class Controller_Kohana_RequestTest_Dummy extends Controller +{ + public function action_index() + { + + } +} // End Kohana_RequestTest diff --git a/system/tests/kohana/ResponseTest.php b/system/tests/kohana/ResponseTest.php new file mode 100644 index 0000000..d5e8870 --- /dev/null +++ b/system/tests/kohana/ResponseTest.php @@ -0,0 +1,208 @@ +getMock('View'); + $view->expects($this->any()) + ->method('__toString') + ->will($this->returnValue('foo')); + + return array( + array('unit test', 'unit test'), + array($view, 'foo'), + ); + } + + /** + * Tests that we can set and read a body of a response + * + * @test + * @dataProvider provider_body + * + * @return null + */ + public function test_body($source, $expected) + { + $response = new Response; + $response->body($source); + $this->assertSame($response->body(), $expected); + + $response = (string) $response; + $this->assertSame($response, $expected); + } + + /** + * Provides data for test_body_string_zero() + * + * @return array + */ + public function provider_body_string_zero() + { + return array( + array('0', '0'), + array("0", '0'), + array(0, '0') + ); + } + + /** + * Test that Response::body() handles numerics correctly + * + * @test + * @dataProvider provider_body_string_zero + * @param string $string + * @param string $expected + * @return void + */ + public function test_body_string_zero($string, $expected) + { + $response = new Response; + $response->body($string); + + $this->assertSame($expected, $response->body()); + } + + /** + * provider for test_cookie_set() + * + * @return array + */ + public function provider_cookie_set() + { + return array( + array( + 'test1', + 'foo', + array( + 'test1' => array( + 'value' => 'foo', + 'expiration' => Cookie::$expiration + ), + ) + ), + array( + array( + 'test2' => 'stfu', + 'test3' => array( + 'value' => 'snafu', + 'expiration' => 123456789 + ) + ), + NULL, + array( + 'test2' => array( + 'value' => 'stfu', + 'expiration' => Cookie::$expiration + ), + 'test3' => array( + 'value' => 'snafu', + 'expiration' => 123456789 + ) + ) + ) + ); + } + + /** + * Tests the Response::cookie() method, ensures + * correct values are set, including defaults + * + * @test + * @dataProvider provider_cookie_set + * @param string $key + * @param string $value + * @param string $expected + * @return void + */ + public function test_cookie_set($key, $value, $expected) + { + // Setup the Response and apply cookie + $response = new Response; + $response->cookie($key, $value); + + foreach ($expected as $_key => $_value) + { + $cookie = $response->cookie($_key); + + $this->assertSame($_value['value'], $cookie['value']); + $this->assertSame($_value['expiration'], $cookie['expiration']); + } + } + + /** + * Tests the Response::cookie() get functionality + * + * @return void + */ + public function test_cookie_get() + { + $response = new Response; + + // Test for empty cookies + $this->assertSame(array(), $response->cookie()); + + // Test for no specific cookie + $this->assertNull($response->cookie('foobar')); + + $response->cookie('foo', 'bar'); + $cookie = $response->cookie('foo'); + + $this->assertSame('bar', $cookie['value']); + $this->assertSame(Cookie::$expiration, $cookie['expiration']); + } + + /** + * Tests that the headers are not sent by PHP in CLI mode + * + * @return void + */ + public function test_send_headers_cli() + { + if (headers_sent()) + { + $this->markTestSkipped('Cannot test this feature as headers have already been sent!'); + } + + $content_type = 'application/json'; + $response = new Response; + $response->headers('content-type', $content_type) + ->send_headers(); + + $this->assertFalse(headers_sent()); + + } + + /** + * Test the content type is sent when set + * + * @test + */ + public function test_content_type_when_set() + { + $content_type = 'application/json'; + $response = new Response; + $response->headers('content-type', $content_type); + $headers = $response->send_headers()->headers(); + $this->assertSame($content_type, (string) $headers['content-type']); + } +} \ No newline at end of file diff --git a/system/tests/kohana/RouteTest.php b/system/tests/kohana/RouteTest.php new file mode 100644 index 0000000..ef37f99 --- /dev/null +++ b/system/tests/kohana/RouteTest.php @@ -0,0 +1,840 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ + +include Kohana::find_file('tests', 'test_data/callback_routes'); + +class Kohana_RouteTest extends Unittest_TestCase +{ + /** + * Remove all caches + */ + // @codingStandardsIgnoreStart + public function setUp() + // @codingStandardsIgnoreEnd + { + parent::setUp(); + + $this->cleanCacheDir(); + } + + /** + * Removes cache files created during tests + */ + // @codingStandardsIgnoreStart + public function tearDown() + // @codingStandardsIgnoreEnd + { + parent::tearDown(); + + $this->cleanCacheDir(); + } + + /** + * If Route::get() is asked for a route that does not exist then + * it should throw a Kohana_Exception + * + * Note use of @expectedException + * + * @test + * @covers Route::get + * @expectedException Kohana_Exception + */ + public function test_get_throws_exception_if_route_dnx() + { + Route::get('HAHAHAHAHAHAHAHAHA'); + } + + /** + * Route::all() should return all routes defined via Route::set() + * and not through new Route() + * + * @test + * @covers Route::all + */ + public function test_all_returns_all_defined_routes() + { + $defined_routes = self::readAttribute('Route', '_routes'); + + $this->assertSame($defined_routes, Route::all()); + } + + /** + * Route::name() should fetch the name of a passed route + * If route is not found then it should return FALSE + * + * @TODO: This test needs to segregate the Route::$_routes singleton + * @test + * @covers Route::name + */ + public function test_name_returns_routes_name_or_false_if_dnx() + { + $route = Route::set('flamingo_people', 'flamingo/dance'); + + $this->assertSame('flamingo_people', Route::name($route)); + + $route = new Route('dance/dance'); + + $this->assertFalse(Route::name($route)); + } + + /** + * If Route::cache() was able to restore routes from the cache then + * it should return TRUE and load the cached routes + * + * @test + * @covers Route::cache + */ + public function test_cache_stores_route_objects() + { + $routes = Route::all(); + + // First we create the cache + Route::cache(TRUE); + + // Now lets modify the "current" routes + Route::set('nonsensical_route', 'flabbadaga/ding_dong'); + + // Then try and load said cache + $this->assertTrue(Route::cache()); + + // Check the route cache flag + $this->assertTrue(Route::$cache); + + // And if all went ok the nonsensical route should be gone... + $this->assertEquals($routes, Route::all()); + } + + /** + * Check appending cached routes. See http://dev.kohanaframework.org/issues/4347 + * + * @test + * @covers Route::cache + */ + public function test_cache_append_routes() + { + $cached = Route::all(); + + // First we create the cache + Route::cache(TRUE); + + // Now lets modify the "current" routes + Route::set('nonsensical_route', 'flabbadaga/ding_dong'); + + $modified = Route::all(); + + // Then try and load said cache + $this->assertTrue(Route::cache(NULL, TRUE)); + + // Check the route cache flag + $this->assertTrue(Route::$cache); + + // And if all went ok the nonsensical route should exist with the other routes... + $this->assertEquals(Route::all(), $cached + $modified); + } + + /** + * Route::cache() should return FALSE if cached routes could not be found + * + * The cache is cleared before and after each test in setUp tearDown + * by cleanCacheDir() + * + * @test + * @covers Route::cache + */ + public function test_cache_returns_false_if_cache_dnx() + { + $this->assertSame(FALSE, Route::cache(), 'Route cache was not empty'); + + // Check the route cache flag + $this->assertFalse(Route::$cache); + } + + /** + * If the constructor is passed a NULL uri then it should assume it's + * being loaded from the cache & therefore shouldn't override the cached attributes + * + * @test + * @covers Route::__construct + */ + public function test_constructor_returns_if_uri_is_null() + { + // We use a mock object to make sure that the route wasn't recompiled + $route = $this->getMock('Route', array('_compile'), array(), '', FALSE); + + $route + ->expects($this->never()) + ->method('_compile'); + + $route->__construct(NULL,NULL); + + $this->assertAttributeSame('', '_uri', $route); + $this->assertAttributeSame(array(), '_regex', $route); + $this->assertAttributeSame(array('action' => 'index', 'host' => FALSE), '_defaults', $route); + $this->assertAttributeSame(NULL, '_route_regex', $route); + } + + /** + * Provider for test_constructor_only_changes_custom_regex_if_passed + * + * @return array + */ + public function provider_constructor_only_changes_custom_regex_if_passed() + { + return array( + array('/', '/'), + ); + } + + /** + * The constructor should only use custom regex if passed a non-empty array + * + * Technically we can't "test" this as the default regex is an empty array, this + * is purely for improving test coverage + * + * @dataProvider provider_constructor_only_changes_custom_regex_if_passed + * + * @test + * @covers Route::__construct + */ + public function test_constructor_only_changes_custom_regex_if_passed($uri, $uri2) + { + $route = new Route($uri, array()); + + $this->assertAttributeSame(array(), '_regex', $route); + + $route = new Route($uri2, NULL); + + $this->assertAttributeSame(array(), '_regex', $route); + } + + /** + * When we pass custom regex to the route's constructor it should it + * in leu of the default. This does not apply to callback/lambda routes + * + * @test + * @covers Route::__construct + * @covers Route::compile + */ + public function test_route_uses_custom_regex_passed_to_constructor() + { + $regex = array('id' => '[0-9]{1,2}'); + + $route = new Route('(/(/))', $regex); + + $this->assertAttributeSame($regex, '_regex', $route); + $this->assertAttributeContains( + $regex['id'], + '_route_regex', + $route + ); + } + + /** + * Provider for test_matches_returns_false_on_failure + * + * @return array + */ + public function provider_matches_returns_false_on_failure() + { + return array( + array('projects/(/((/(/))))', 'apple/pie'), + ); + } + + /** + * Route::matches() should return false if the route doesn't match against a uri + * + * @dataProvider provider_matches_returns_false_on_failure + * + * @test + * @covers Route::matches + */ + public function test_matches_returns_false_on_failure($uri, $match) + { + $route = new Route($uri); + + // Mock a request class with the $match uri + $stub = $this->getMock('Request', array('uri'), array($match)); + $stub->expects($this->any()) + ->method('uri') + // Request::uri() called by Route::matches() will return $match + ->will($this->returnValue($match)); + + $this->assertSame(FALSE, $route->matches($stub)); + } + + /** + * Provider for test_matches_returns_array_of_parameters_on_successful_match + * + * @return array + */ + public function provider_matches_returns_array_of_parameters_on_successful_match() + { + return array( + array( + '((/(/)))', + 'welcome/index', + 'Welcome', + 'index', + ), + ); + } + + /** + * Route::matches() should return an array of parameters when a match is made + * An parameters that are not matched should not be present in the array of matches + * + * @dataProvider provider_matches_returns_array_of_parameters_on_successful_match + * + * @test + * @covers Route::matches + */ + public function test_matches_returns_array_of_parameters_on_successful_match($uri, $m, $c, $a) + { + $route = new Route($uri); + + // Mock a request class with the $m uri + $request = $this->getMock('Request', array('uri'), array($m)); + $request->expects($this->any()) + ->method('uri') + // Request::uri() called by Route::matches() will return $m + ->will($this->returnValue($m)); + + $matches = $route->matches($request); + + $this->assertInternalType('array', $matches); + $this->assertArrayHasKey('controller', $matches); + $this->assertArrayHasKey('action', $matches); + $this->assertArrayNotHasKey('id', $matches); + // $this->assertSame(5, count($matches)); + $this->assertSame($c, $matches['controller']); + $this->assertSame($a, $matches['action']); + } + + /** + * Provider for test_matches_returns_array_of_parameters_on_successful_match + * + * @return array + */ + public function provider_defaults_are_used_if_params_arent_specified() + { + return array( + array( + '(/(/))', + NULL, + array('controller' => 'Welcome', 'action' => 'index'), + 'Welcome', + 'index', + 'unit/test/1', + array( + 'controller' => 'unit', + 'action' => 'test', + 'id' => '1' + ), + 'Welcome', + ), + array( + '((/(/)))', + NULL, + array('controller' => 'welcome', 'action' => 'index'), + 'Welcome', + 'index', + 'unit/test/1', + array( + 'controller' => 'unit', + 'action' => 'test', + 'id' => '1' + ), + '', + ), + /** + * Specifying this should cause controller and action to show up + * refs #4113 + */ + array( + '((/(/)))', + NULL, + array('controller' => 'welcome', 'action' => 'index'), + 'Welcome', + 'index', + 'welcome/index/1', + array( + 'id' => '1' + ), + '', + ), + array( + '(/(/))', + NULL, + array('controller' => 'welcome', 'action' => 'index'), + 'Welcome', + 'index', + 'welcome/foo', + array( + 'action' => 'foo', + ), + 'welcome', + ), + ); + } + + /** + * Defaults specified with defaults() should be used if their values aren't + * present in the uri + * + * @dataProvider provider_defaults_are_used_if_params_arent_specified + * + * @test + * @covers Route::matches + */ + public function test_defaults_are_used_if_params_arent_specified($uri, $regex, $defaults, $c, $a, $test_uri, $test_uri_array, $default_uri) + { + $route = new Route($uri, $regex); + $route->defaults($defaults); + + $this->assertSame($defaults, $route->defaults()); + + // Mock a request class + $request = $this->getMock('Request', array('uri'), array($default_uri)); + $request->expects($this->any()) + ->method('uri') + ->will($this->returnValue($default_uri)); + + $matches = $route->matches($request); + + $this->assertInternalType('array', $matches); + $this->assertArrayHasKey('controller', $matches); + $this->assertArrayHasKey('action', $matches); + $this->assertArrayNotHasKey('id', $matches); + // $this->assertSame(4, count($matches)); + $this->assertSame($c, $matches['controller']); + $this->assertSame($a, $matches['action']); + $this->assertSame($test_uri, $route->uri($test_uri_array)); + $this->assertSame($default_uri, $route->uri()); + } + + /** + * Optional params should not be used if what is passed in is identical + * to the default. + * + * refs #4116 + * + * @test + * @covers Route::uri + */ + public function test_defaults_are_not_used_if_param_is_identical() + { + $route = new Route('((/(/)))'); + $route->defaults(array( + 'controller' => 'welcome', + 'action' => 'index' + )); + + $this->assertSame('', $route->uri(array('controller' => 'welcome'))); + $this->assertSame('welcome2', $route->uri(array('controller' => 'welcome2'))); + } + + /** + * Provider for test_required_parameters_are_needed + * + * @return array + */ + public function provider_required_parameters_are_needed() + { + return array( + array( + 'admin(/(/(/)))', + 'admin', + 'admin/users/add', + ), + ); + } + + /** + * This tests that routes with required parameters will not match uris without them present + * + * @dataProvider provider_required_parameters_are_needed + * + * @test + * @covers Route::matches + */ + public function test_required_parameters_are_needed($uri, $matches_route1, $matches_route2) + { + $route = new Route($uri); + + // Mock a request class that will return empty uri + $request = $this->getMock('Request', array('uri'), array('')); + $request->expects($this->any()) + ->method('uri') + ->will($this->returnValue('')); + + $this->assertFalse($route->matches($request)); + + // Mock a request class that will return route1 + $request = $this->getMock('Request', array('uri'), array($matches_route1)); + $request->expects($this->any()) + ->method('uri') + ->will($this->returnValue($matches_route1)); + + $matches = $route->matches($request); + + $this->assertInternalType('array', $matches); + + // Mock a request class that will return route2 uri + $request = $this->getMock('Request', array('uri'), array($matches_route2)); + $request->expects($this->any()) + ->method('uri') + ->will($this->returnValue($matches_route2)); + + $matches = $route->matches($request); + + $this->assertInternalType('array', $matches); + // $this->assertSame(5, count($matches)); + $this->assertArrayHasKey('controller', $matches); + $this->assertArrayHasKey('action', $matches); + } + + /** + * Provider for test_required_parameters_are_needed + * + * @return array + */ + public function provider_reverse_routing_returns_routes_uri_if_route_is_static() + { + return array( + array( + 'info/about_us', + NULL, + 'info/about_us', + array('some' => 'random', 'params' => 'to confuse'), + ), + ); + } + + /** + * This tests the reverse routing returns the uri specified in the route + * if it's a static route + * + * A static route is a route without any parameters + * + * @dataProvider provider_reverse_routing_returns_routes_uri_if_route_is_static + * + * @test + * @covers Route::uri + */ + public function test_reverse_routing_returns_routes_uri_if_route_is_static($uri, $regex, $target_uri, $uri_params) + { + $route = new Route($uri, $regex); + + $this->assertSame($target_uri, $route->uri($uri_params)); + } + + /** + * Provider for test_uri_throws_exception_if_required_params_are_missing + * + * @return array + */ + public function provider_uri_throws_exception_if_required_params_are_missing() + { + return array( + array( + '(/ 'awesome-action'), + ), + ); + } + + /** + * When Route::uri is working on a uri that requires certain parameters to be present + * (i.e. in 'uri($uri_array); + + $this->fail('Route::uri should throw exception if required param is not provided'); + } + catch(Exception $e) + { + $this->assertInstanceOf('Kohana_Exception', $e); + // Check that the error in question is about the controller param + $this->assertContains('controller', $e->getMessage()); + } + } + + /** + * Provider for test_uri_fills_required_uri_segments_from_params + * + * @return array + */ + public function provider_uri_fills_required_uri_segments_from_params() + { + return array( + array( + '/(/)', + NULL, + 'users/edit', + array( + 'controller' => 'users', + 'action' => 'edit', + ), + 'users/edit/god', + array( + 'controller' => 'users', + 'action' => 'edit', + 'id' => 'god', + ), + ), + ); + } + + /** + * The logic for replacing required segments is separate (but similar) to that for + * replacing optional segments. + * + * This test asserts that Route::uri will replace required segments with provided + * params + * + * @dataProvider provider_uri_fills_required_uri_segments_from_params + * + * @test + * @covers Route::uri + */ + public function test_uri_fills_required_uri_segments_from_params($uri, $regex, $uri_string1, $uri_array1, $uri_string2, $uri_array2) + { + $route = new Route($uri, $regex); + + $this->assertSame( + $uri_string1, + $route->uri($uri_array1) + ); + + $this->assertSame( + $uri_string2, + $route->uri($uri_array2) + ); + } + + /** + * Provides test data for test_composing_url_from_route() + * @return array + */ + public function provider_composing_url_from_route() + { + return array( + array('/'), + array('/news/view/42', array('controller' => 'news', 'action' => 'view', 'id' => 42)), + array('http://kohanaframework.org/news', array('controller' => 'news'), 'http') + ); + } + + /** + * Tests Route::url() + * + * Checks the url composing from specific route via Route::url() shortcut + * + * @test + * @dataProvider provider_composing_url_from_route + * @param string $expected + * @param array $params + * @param boolean $protocol + */ + public function test_composing_url_from_route($expected, $params = NULL, $protocol = NULL) + { + Route::set('foobar', '((/(/)))') + ->defaults(array( + 'controller' => 'welcome', + ) + ); + + $this->setEnvironment(array( + '_SERVER' => array('HTTP_HOST' => 'kohanaframework.org'), + 'Kohana::$base_url' => '/', + 'Kohana::$index_file' => '', + )); + + $this->assertSame($expected, Route::url('foobar', $params, $protocol)); + } + + /** + * Tests Route::compile() + * + * Makes sure that compile will use custom regex if specified + * + * @test + * @covers Route::compile + */ + public function test_compile_uses_custom_regex_if_specificed() + { + $compiled = Route::compile( + '(/(/))', + array( + 'controller' => '[a-z]+', + 'id' => '\d+', + ) + ); + + $this->assertSame('#^(?P[a-z]+)(?:/(?P[^/.,;?\n]++)(?:/(?P\d+))?)?$#uD', $compiled); + } + + /** + * Tests Route::is_external(), ensuring the host can return + * whether internal or external host + */ + public function test_is_external_route_from_host() + { + // Setup local route + Route::set('internal', 'local/test/route') + ->defaults(array( + 'controller' => 'foo', + 'action' => 'bar' + ) + ); + + // Setup external route + Route::set('external', 'local/test/route') + ->defaults(array( + 'controller' => 'foo', + 'action' => 'bar', + 'host' => 'http://kohanaframework.org' + ) + ); + + // Test internal route + $this->assertFalse(Route::get('internal')->is_external()); + + // Test external route + $this->assertTrue(Route::get('external')->is_external()); + } + + /** + * Provider for test_external_route_includes_params_in_uri + * + * @return array + */ + public function provider_external_route_includes_params_in_uri() + { + return array( + array( + '/', + array( + 'controller' => 'foo', + 'action' => 'bar', + 'host' => 'kohanaframework.org' + ), + 'http://kohanaframework.org/foo/bar' + ), + array( + '/', + array( + 'controller' => 'foo', + 'action' => 'bar', + 'host' => 'http://kohanaframework.org' + ), + 'http://kohanaframework.org/foo/bar' + ), + array( + 'foo/bar', + array( + 'controller' => 'foo', + 'host' => 'http://kohanaframework.org' + ), + 'http://kohanaframework.org/foo/bar' + ), + ); + } + + /** + * Tests the external route include route parameters + * + * @dataProvider provider_external_route_includes_params_in_uri + */ + public function test_external_route_includes_params_in_uri($route, $defaults, $expected_uri) + { + Route::set('test', $route) + ->defaults($defaults); + + $this->assertSame($expected_uri, Route::get('test')->uri()); + } + + /** + * Provider for test_route_filter_modify_params + * + * @return array + */ + public function provider_route_filter_modify_params() + { + return array( + array( + '/', + array( + 'controller' => 'Test', + 'action' => 'same', + ), + array('Route_Holder', 'route_filter_modify_params_array'), + 'test/different', + array( + 'controller' => 'Test', + 'action' => 'modified', + ), + ), + array( + '/', + array( + 'controller' => 'test', + 'action' => 'same', + ), + array('Route_Holder', 'route_filter_modify_params_false'), + 'test/fail', + FALSE, + ), + ); + } + + /** + * Tests that route filters can modify parameters + * + * @covers Route::filter + * @dataProvider provider_route_filter_modify_params + */ + public function test_route_filter_modify_params($route, $defaults, $filter, $uri, $expected_params) + { + $route = new Route($route); + + // Mock a request class + $request = $this->getMock('Request', array('uri'), array($uri)); + $request->expects($this->any()) + ->method('uri') + ->will($this->returnValue($uri)); + + $params = $route->defaults($defaults)->filter($filter)->matches($request); + + $this->assertSame($expected_params, $params); + } + +} diff --git a/system/tests/kohana/SecurityTest.php b/system/tests/kohana/SecurityTest.php new file mode 100644 index 0000000..4c3b368 --- /dev/null +++ b/system/tests/kohana/SecurityTest.php @@ -0,0 +1,108 @@ +"), + ); + } + + /** + * Tests Security::encode_php_tags() + * + * @test + * @dataProvider provider_encode_php_tags + * @covers Security::encode_php_tags + */ + public function test_encode_php_tags($expected, $input) + { + $this->assertSame($expected, Security::encode_php_tags($input)); + } + + /** + * Provides test data for test_strip_image_tags() + * + * @return array Test data sets + */ + public function provider_strip_image_tags() + { + return array( + array('foo', ''), + ); + } + + /** + * Tests Security::strip_image_tags() + * + * @test + * @dataProvider provider_strip_image_tags + * @covers Security::strip_image_tags + */ + public function test_strip_image_tags($expected, $input) + { + $this->assertSame($expected, Security::strip_image_tags($input)); + } + + /** + * Provides test data for Security::token() + * + * @return array Test data sets + */ + public function provider_csrf_token() + { + // Unfortunately this data provider has to use the session in order to + // generate its data. If headers have already been sent then this method + // throws an error, even if the test is does not run. If we return an + // empty array then this also causes an error, so the only way to get + // around it is to return an array of misc data and have the test skip + // if headers have been sent. It's annoying this hack has to be + // implemented, but the security code isn't exactly brilliantly + // implemented. Ideally we'd be able to inject a session instance + if (headers_sent()) + return array(array('', '', 0)); + + $array = array(); + for ($i = 0; $i <= 4; $i++) + { + Security::$token_name = 'token_'.$i; + $array[] = array(Security::token(TRUE), Security::check(Security::token(FALSE)), $i); + } + return $array; + } + + /** + * Tests Security::token() + * + * @test + * @dataProvider provider_csrf_token + * @covers Security::token + */ + public function test_csrf_token($expected, $input, $iteration) + { + if (headers_sent()) { + $this->markTestSkipped('Headers have already been sent, session not available'); + } + + Security::$token_name = 'token_'.$iteration; + $this->assertSame(TRUE, $input); + $this->assertSame($expected, Security::token(FALSE)); + Session::instance()->delete(Security::$token_name); + } +} diff --git a/system/tests/kohana/SessionTest.php b/system/tests/kohana/SessionTest.php new file mode 100644 index 0000000..1f34fa8 --- /dev/null +++ b/system/tests/kohana/SessionTest.php @@ -0,0 +1,502 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_SessionTest extends Unittest_TestCase +{ + + /** + * Gets a mock of the session class + * + * @return Session + */ + // @codingStandardsIgnoreStart + public function getMockSession(array $config = array()) + // @codingStandardsIgnoreEnd + { + return $this->getMockForAbstractClass('Session', array($config)); + } + + /** + * Provides test data for + * + * test_constructor_uses_name_from_config_and_casts() + * + * @return array + */ + public function provider_constructor_uses_settings_from_config_and_casts() + { + return array( + // array(expected, input) + // data set 0 + array( + array( + 'name' => 'awesomeness', + 'lifetime' => 1231456421, + 'encrypted' => FALSE + ), + array( + 'name' => 'awesomeness', + 'lifetime' => '1231456421', + 'encrypted' => FALSE, + ), + ), + // data set 1 + array( + array( + 'name' => '123', + 'encrypted' => 'default', + ), + array( + 'name' => 123, + 'encrypted' => TRUE, + ), + ), + ); + } + + /** + * The constructor should change its attributes based on config + * passed as the first parameter + * + * @test + * @dataProvider provider_constructor_uses_settings_from_config_and_casts + * @covers Session::__construct + */ + public function test_constructor_uses_settings_from_config_and_casts($expected, $config) + { + $session = $this->getMockForAbstractClass('Session', array($config)); + + foreach ($expected as $var => $value) + { + $this->assertAttributeSame($value, '_'.$var, $session); + } + } + + /** + * Check that the constructor will load a session if it's provided + * witha session id + * + * @test + * @covers Session::__construct + * @covers Session::read + */ + public function test_constructor_loads_session_with_session_id() + { + $this->markTestIncomplete( + 'Need to work out why constructor is not being called' + ); + + $config = array(); + $session_id = 'lolums'; + + // Don't auto-call constructor, we need to setup the mock first + $session = $this->getMockForAbstractClass( + 'Session', + array(), + '', + FALSE + ); + + $session + ->expects($this->once()) + ->method('read') + ->with($session_id); + + $session->__construct($config, $session_id); + } + + /** + * Calling $session->bind() should allow you to bind a variable + * to a session variable + * + * @test + * @covers Session::bind + * @ticket 3164 + */ + public function test_bind_actually_binds_variable() + { + $session = $this->getMockForAbstractClass('Session'); + + $var = 'asd'; + + $session->bind('our_var', $var); + + $var = 'foobar'; + + $this->assertSame('foobar', $session->get('our_var')); + } + + + /** + * When a session is initially created it should have no data + * + * + * @test + * @covers Session::__construct + * @covers Session::set + */ + public function test_initially_session_has_no_data() + { + $session = $this->getMockSession(); + + $this->assertAttributeSame(array(), '_data', $session); + } + + /** + * Make sure that the default session name (the one used if the + * driver does not set one) is 'session' + * + * @test + * @covers Session::__construct + */ + public function test_default_session_name_is_set() + { + $session = $this->getMockSession(); + + $this->assertAttributeSame('session', '_name', $session); + } + + /** + * By default sessions are unencrypted + * + * @test + * @covers Session::__construct + */ + public function test_default_session_is_unencrypted() + { + $session = $this->getMockSession(); + + $this->assertAttributeSame(FALSE, '_encrypted', $session); + } + + /** + * A new session should not be classed as destroyed + * + * @test + * @covers Session::__construct + */ + public function test_default_session_is_not_classed_as_destroyed() + { + $session = $this->getMockSession(); + + $this->assertAttributeSame(FALSE, '_destroyed', $session); + } + + /** + * Provides test data for test_get_returns_default_if_var_dnx() + * + * @return array + */ + public function provider_get_returns_default_if_var_dnx() + { + return array( + array('something_crazy', FALSE), + array('a_true', TRUE), + array('an_int', 158163158), + ); + } + + /** + * Make sure that get() is using the default value we provide and + * isn't tampering with it + * + * @test + * @dataProvider provider_get_returns_default_if_var_dnx + * @covers Session::get + */ + public function test_get_returns_default_if_var_dnx($var, $default) + { + $session = $this->getMockSession(); + + $this->assertSame($default, $session->get($var, $default)); + } + + /** + * By default get() should be using null as the var DNX return value + * + * @test + * @covers Session::get + */ + public function test_get_uses_null_as_default_return_value() + { + $session = $this->getMockSession(); + + $this->assertSame(NULL, $session->get('level_of_cool')); + } + + /** + * This test makes sure that session is using array_key_exists + * as isset will return FALSE if the value is NULL + * + * @test + * @covers Session::get + */ + public function test_get_returns_value_if_it_equals_null() + { + $session = $this->getMockSession(); + + $session->set('arkward', NULL); + + $this->assertSame(NULL, $session->get('arkward', 'uh oh')); + } + + /** + * as_array() should return the session data by reference. + * + * i.e. if we modify the returned data, the session data also changes + * + * @test + * @covers Session::as_array + */ + public function test_as_array_returns_data_by_ref_or_copy() + { + $session = $this->getMockSession(); + + $data_ref =& $session->as_array(); + + $data_ref['something'] = 'pie'; + + $this->assertAttributeSame($data_ref, '_data', $session); + + $data_copy = $session->as_array(); + + $data_copy['pie'] = 'awesome'; + + $this->assertAttributeNotSame($data_copy, '_data', $session); + } + + /** + * set() should add new session data and modify existing ones + * + * Also makes sure that set() returns $this + * + * @test + * @covers Session::set + */ + public function test_set_adds_and_modifies_to_session_data() + { + $session = $this->getMockSession(); + + $this->assertSame($session, $session->set('pork', 'pie')); + + $this->assertAttributeSame( + array('pork' => 'pie'), + '_data', + $session + ); + + $session->set('pork', 'delicious'); + + $this->assertAttributeSame( + array('pork' => 'delicious'), + '_data', + $session + ); + } + + /** + * This tests that delete() removes specified session data + * + * @test + * @covers Session::delete + */ + public function test_delete_removes_select_session_data() + { + $session = $this->getMockSession(); + + // Bit of a hack for mass-loading session data + $data =& $session->as_array(); + + $data += array( + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'easy' => '123' + ); + + // Make a copy of $data for testing purposes + $copy = $data; + + // First we make sure we can delete one item + // Also, check that delete returns $this + $this->assertSame($session, $session->delete('a')); + + unset($copy['a']); + + // We could test against $data but then we'd be testing + // that as_array() is returning by ref + $this->assertAttributeSame($copy, '_data', $session); + + // Now we make sure we can delete multiple items + // We're checking $this is returned just in case + $this->assertSame($session, $session->delete('b', 'c')); + unset($copy['b'], $copy['c']); + + $this->assertAttributeSame($copy, '_data', $session); + } + + /** + * Provides test data for test_read_loads_session_data() + * + * @return array + */ + public function provider_read_loads_session_data() + { + return array( + // If driver returns array then just load it up + array( + array(), + 'wacka_wacka', + array() + ), + array( + array('the it' => 'crowd'), + 'the_it_crowd', + array('the it' => 'crowd'), + ), + // If it's a string an encrpytion is disabled (by default) base64decode and unserialize + array( + array('dead' => 'arrival'), + 'lolums', + 'YToxOntzOjQ6ImRlYWQiO3M6NzoiYXJyaXZhbCI7fQ==' + ), + ); + } + + /** + * This is one of the "big" tests for the session lib + * + * The test makes sure that + * + * 1. Session asks the driver for the data relating to $session_id + * 2. That it will load the returned data into the session + * + * @test + * @dataProvider provider_read_loads_session_data + * @covers Session::read + */ + public function test_read_loads_session_data($expected_data, $session_id, $driver_data, array $config = array()) + { + $session = $this->getMockSession($config); + + $session->expects($this->once()) + ->method('_read') + ->with($session_id) + ->will($this->returnValue($driver_data)); + + $session->read($session_id); + $this->assertAttributeSame($expected_data, '_data', $session); + } + + /** + * regenerate() should tell the driver to regenerate its id + * + * @test + * @covers Session::regenerate + */ + public function test_regenerate_tells_driver_to_regenerate() + { + $session = $this->getMockSession(); + + $new_session_id = 'asdnoawdnoainf'; + + $session->expects($this->once()) + ->method('_regenerate') + ->with() + ->will($this->returnValue($new_session_id)); + + $this->assertSame($new_session_id, $session->regenerate()); + } + + /** + * If the driver destroys the session then all session data should be + * removed + * + * @test + * @covers Session::destroy + */ + public function test_destroy_deletes_data_if_driver_destroys_session() + { + $session = $this->getMockSession(); + + $session + ->set('asd', 'dsa') + ->set('dog', 'god'); + + $session + ->expects($this->once()) + ->method('_destroy') + ->with() + ->will($this->returnValue(TRUE)); + + $this->assertTrue($session->destroy()); + + $this->assertAttributeSame(array(), '_data', $session); + } + + /** + * The session data should only be deleted if the driver reports + * that the session was destroyed ok + * + * @test + * @covers Session::destroy + */ + public function test_destroy_only_deletes_data_if_driver_destroys_session() + { + $session = $this->getMockSession(); + + $session + ->set('asd', 'dsa') + ->set('dog', 'god'); + + $session + ->expects($this->once()) + ->method('_destroy') + ->with() + ->will($this->returnValue(FALSE)); + + $this->assertFalse($session->destroy()); + $this->assertAttributeSame( + array('asd' => 'dsa', 'dog' => 'god'), + '_data', + $session + ); + } + + /** + * If a session variable exists then get_once should get it then remove it. + * If the variable does not exist then it should return the default + * + * @test + * @covers Session::get_once + */ + public function test_get_once_gets_once_or_returns_default() + { + $session = $this->getMockSession(); + + $session->set('foo', 'bar'); + + // Test that a default is returned + $this->assertSame('mud', $session->get_once('fud', 'mud')); + + // Now test that it actually removes the value + $this->assertSame('bar', $session->get_once('foo')); + + $this->assertAttributeSame(array(), '_data', $session); + + $this->assertSame('maybe', $session->get_once('foo', 'maybe')); + } +} diff --git a/system/tests/kohana/TextTest.php b/system/tests/kohana/TextTest.php new file mode 100644 index 0000000..a59bd59 --- /dev/null +++ b/system/tests/kohana/TextTest.php @@ -0,0 +1,642 @@ +assertSame('', Text::auto_p('')); + } + + /** + * + * @return array Test Data + */ + function provider_auto_para_does_not_enclose_html_tags_in_paragraphs() + { + return array( + array( + array('div'), + '
      Pick a plum of peppers
      ', + ), + array( + array('div'), + '
      Tangas
      ', + ), + ); + } + + /** + * This test makes sure that auto_p doesn't enclose HTML tags + * in paragraphs + * + * @test + * @covers Text::auto_p + * @dataProvider provider_auto_para_does_not_enclose_html_tags_in_paragraphs + */ + function test_auto_para_does_not_enclose_html_tags_in_paragraphs(array $tags, $text) + { + $output = Text::auto_p($text); + + foreach ($tags as $tag) + { + $this->assertNotTag( + array('tag' => $tag, 'ancestor' => array('tag' => 'p')), + $output + ); + } + } + + /** + * This test makes sure that auto_p surrounds a single line of text + * with paragraph tags + * + * @test + * @covers Text::auto_p + */ + function test_auto_para_encloses_slot_in_paragraph() + { + $text = 'Pick a pinch of purple pepper'; + + $this->assertSame('

      '.$text.'

      ', Text::auto_p($text)); + } + + /** + * Make sure that multiple new lines are replaced with paragraph tags + * + * @test + * @covers Text::auto_p + */ + public function test_auto_para_replaces_multiple_newlines_with_paragraph() + { + $this->assertSame( + "

      My name is john

      \n\n

      I'm a developer

      ", + Text::auto_p("My name is john\n\n\n\nI'm a developer") + ); + } + + /** + * Data provider for test_limit_words + * + * @return array Array of test data + */ + function provider_limit_words() + { + return array + ( + array('', '', 100, NULL), + array('…', 'The rain in spain', -10, NULL), + array('The rain…', 'The rain in spain', 2, NULL), + array('The rain...', 'The rain in spain', 2, '...'), + ); + } + + /** + * + * @test + * @dataProvider provider_limit_words + */ + function test_limit_words($expected, $str, $limit, $end_char) + { + $this->assertSame($expected, Text::limit_words($str, $limit, $end_char)); + } + + /** + * Provides test data for test_limit_chars() + * + * @return array Test data + */ + function provider_limit_chars() + { + return array + ( + array('', '', 100, NULL, FALSE), + array('…', 'BOO!', -42, NULL, FALSE), + array('making php bet…', 'making php better for the sane', 14, NULL, FALSE), + array('Garçon! Un café s.v.p.', 'Garçon! Un café s.v.p.', 50, '__', FALSE), + array('Garçon!__', 'Garçon! Un café s.v.p.', 8, '__', FALSE), + // @issue 3238 + array('making php…', 'making php better for the sane', 14, NULL, TRUE), + array('Garçon!__', 'Garçon! Un café s.v.p.', 9, '__', TRUE), + array('Garçon!__', 'Garçon! Un café s.v.p.', 7, '__', TRUE), + array('__', 'Garçon! Un café s.v.p.', 5, '__', TRUE), + ); + } + + /** + * Tests Text::limit_chars() + * + * @test + * @dataProvider provider_limit_chars + */ + function test_limit_chars($expected, $str, $limit, $end_char, $preserve_words) + { + $this->assertSame($expected, Text::limit_chars($str, $limit, $end_char, $preserve_words)); + } + + /** + * Test Text::alternate() + * + * @test + */ + function test_alternate_alternates_between_parameters() + { + list($val_a, $val_b, $val_c) = array('good', 'bad', 'ugly'); + + $this->assertSame('good', Text::alternate($val_a, $val_b, $val_c)); + $this->assertSame('bad', Text::alternate($val_a, $val_b, $val_c)); + $this->assertSame('ugly', Text::alternate($val_a, $val_b, $val_c)); + + $this->assertSame('good', Text::alternate($val_a, $val_b, $val_c)); + } + + /** + * Tests Text::alternate() + * + * @test + * @covers Text::alternate + */ + function test_alternate_resets_when_called_with_no_params_and_returns_empty_string() + { + list($val_a, $val_b, $val_c) = array('yes', 'no', 'maybe'); + + $this->assertSame('yes', Text::alternate($val_a, $val_b, $val_c)); + + $this->assertSame('', Text::alternate()); + + $this->assertSame('yes', Text::alternate($val_a, $val_b, $val_c)); + } + + /** + * Provides test data for test_reducde_slashes() + * + * @returns array Array of test data + */ + function provider_reduce_slashes() + { + return array + ( + array('/', '//'), + array('/google/php/kohana/', '//google/php//kohana//'), + ); + } + + /** + * Covers Text::reduce_slashes() + * + * @test + * @dataProvider provider_reduce_slashes + */ + function test_reduce_slashes($expected, $str) + { + $this->assertSame($expected, Text::reduce_slashes($str)); + } + + /** + * Provides test data for test_censor() + * + * @return array Test data + */ + function provider_censor() + { + + return array + ( + // If the replacement is 1 character long it should be repeated for the length of the removed word + array("A donkey is also an ***", 'A donkey is also an ass', array('ass'), '*', TRUE), + array("Cake### isn't nearly as good as kohana###", "CakePHP isn't nearly as good as kohanaphp", array('php'), '#', TRUE), + // If it's > 1 then it's just replaced straight out + array("If you're born out of wedlock you're a --expletive--", "If you're born out of wedlock you're a child", array('child'), '--expletive--', TRUE), + + array('class', 'class', array('ass'), '*', FALSE), + ); + } + + /** + * Tests Text::censor + * + * @test + * @dataProvider provider_censor + */ + function test_censor($expected, $str, $badwords, $replacement, $replace_partial_words) + { + $this->assertSame($expected, Text::censor($str, $badwords, $replacement, $replace_partial_words)); + } + + /** + * Provides test data for test_random + * + * @return array Test Data + */ + function provider_random() + { + return array( + array('alnum', 8), + array('alpha', 10), + array('hexdec', 20), + array('nozero', 5), + array('numeric', 14), + array('distinct', 12), + array('aeiou', 4), + array('‹¡›«¿»', 8), // UTF8 characters + array(NULL, 8), // Issue #3256 + ); + } + + /** + * Tests Text::random() as well as possible + * + * Obviously you can't compare a randomly generated string against a + * pre-generated one and check that they are the same as this goes + * against the whole ethos of random. + * + * This test just makes sure that the value returned is of the correct + * values and length + * + * @test + * @dataProvider provider_random + */ + function test_random($type, $length) + { + if ($type === NULL) + { + $type = 'alnum'; + } + + $pool = (string) $type; + + switch ($pool) + { + case 'alnum': + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + break; + case 'alpha': + $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + break; + case 'hexdec': + $pool = '0123456789abcdef'; + break; + case 'numeric': + $pool = '0123456789'; + break; + case 'nozero': + $pool = '123456789'; + break; + case 'distinct': + $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ'; + break; + } + + $this->assertRegExp('/^['.$pool.']{'.$length.'}$/u', Text::random($type, $length)); + } + + /** + * Provides test data for test_similar + * + * @return array + */ + function provider_similar() + { + return array + ( + // TODO: add some more cases + array('foo', array('foobar', 'food', 'fooberry')), + ); + } + + /** + * Tests Text::similar() + * + * @test + * @dataProvider provider_similar + * @covers Text::similar + */ + function test_similar($expected, $words) + { + $this->assertSame($expected, Text::similar($words)); + } + + /** + * Provides test data for test_bytes + * + * @return array + */ + public function provider_bytes() + { + return array + ( + // TODO: cover the other units + array('256.00 B', 256, NULL, NULL, TRUE), + array('1.02 kB', 1024, NULL, NULL, TRUE), + + // In case you need to know the size of a floppy disk in petabytes + array('0.00147 GB', 1.44 * 1000 * 1024, 'GB', '%01.5f %s', TRUE), + + // SI is the standard, but lets deviate slightly + array('1.00 MiB', 1024 * 1024, 'MiB', NULL, FALSE), + ); + } + + /** + * Tests Text::bytes() + * + * @test + * @dataProvider provider_bytes + */ + function test_bytes($expected, $bytes, $force_unit, $format, $si) + { + $this->assertSame($expected, Text::bytes($bytes, $force_unit, $format, $si)); + } + + /** + * Provides test data for test_widont() + * + * @return array Test data + */ + function provider_widont() + { + return array + ( + array('No gain, no pain', 'No gain, no pain'), + array("spaces?what'rethey?", "spaces?what'rethey?"), + array('', ''), + ); + } + + /** + * Tests Text::widont() + * + * @test + * @dataProvider provider_widont + */ + function test_widont($expected, $string) + { + $this->assertSame($expected, Text::widont($string)); + } + + + /** + * This checks that auto_link_emails() respects word boundaries and does not + * just blindly replace all occurences of the email address in the text. + * + * In the sample below the algorithm was replacing all occurences of voorzitter@xxxx.com + * inc the copy in the second list item. + * + * It was updated in 6c199366efc1115545ba13108b876acc66c54b2d to respect word boundaries + * + * @test + * @covers Text::auto_link_emails + * @ticket 2772 + */ + function test_auto_link_emails_respects_word_boundaries() + { + $original = '
        +
      • voorzitter@xxxx.com
      • +
      • vicevoorzitter@xxxx.com
      • +
      '; + + $this->assertFalse(strpos('vice', Text::auto_link_emails($original))); + } + + + /** + * Provides some test data for test_number() + * + * @return array + */ + public function provider_number() + { + return array( + array('one', 1), + array('twenty-three', 23), + array('fourty-two', 42), + array('five million, six hundred and thirty-two', 5000632), + array('five million, six hundred and thirty', 5000630), + array('nine hundred million', 900000000), + array('thirty-seven thousand', 37000), + array('one thousand and twenty-four', 1024), + ); + } + + /** + * Checks that Text::number formats a number into english text + * + * @test + * @dataProvider provider_number + */ + public function test_number($expected, $number) + { + $this->assertSame($expected, Text::number($number)); + } + + /** + * Provides test data for test_auto_link_urls() + * + * @return array + */ + public function provider_auto_link_urls() + { + return array( + // First we try with the really obvious url + array( + 'Some random text http://www.google.com', + 'Some random text http://www.google.com', + ), + // Then we try with varying urls + array( + 'Some random www.google.com', + 'Some random www.google.com', + ), + array( + 'Some random google.com', + 'Some random google.com', + ), + // Check that it doesn't link urls in a href + array( + 'Look at me Awesome stuff', + 'Look at me Awesome stuff', + ), + array( + 'Look at me http://www.google.com', + 'Look at me http://www.google.com', + ), + // Punctuation at the end of the URL + array( + 'Wow http://www.google.com!', + 'Wow http://www.google.com!', + ), + array( + 'Zomg www.google.com!', + 'Zomg www.google.com!', + ), + array( + 'Well this, www.google.com, is cool', + 'Well this, www.google.com, is cool', + ), + // @issue 3190 + array( + 'www.google.com', + 'www.google.com', + ), + array( + 'www.google.com http://www.google.com/', + 'www.google.com http://www.google.com/', + ), + // @issue 3436 + array( + 'http://www.google.com/', + 'http://www.google.com/', + ), + // @issue 4208, URLs with a path + array( + 'Foobar www.google.com/analytics cake', + 'Foobar www.google.com/analytics cake', + ), + array( + 'Look at this www.google.com/analytics!', + 'Look at this www.google.com/analytics!', + ), + array( + 'Path http://www.google.com/analytics works?', + 'Path http://www.google.com/analytics works?', + ), + array( + 'Path http://www.google.com/analytics', + 'Path http://www.google.com/analytics', + ), + array( + 'Path www.google.com/analytics', + 'Path www.google.com/analytics', + ), + ); + } + + /** + * Runs tests for Test::auto_link_urls + * + * @test + * @dataProvider provider_auto_link_urls + */ + public function test_auto_link_urls($expected, $text) + { + $this->assertSame($expected, Text::auto_link_urls($text)); + } + + /** + * Provides test data for test_auto_link_emails() + * + * @return array + */ + public function provider_auto_link_emails() + { + return array( + // @issue 3162 + array( + 'info@test.com', + 'info@test.com', + ), + array( + 'info@test.com', + 'info@test.com', + ), + // @issue 3189 + array( + 'email@address.com email@address.com', + 'email@address.com email@address.com', + ), + ); + } + + /** + * Runs tests for Test::auto_link_emails + * + * @test + * @dataProvider provider_auto_link_emails + */ + public function test_auto_link_emails($expected, $text) + { + // Use html_entity_decode because emails will be randomly encoded by HTML::mailto + $this->assertSame($expected, html_entity_decode(Text::auto_link_emails($text))); + } + + /** + * Provides test data for test_auto_link + * + * @return array Test data + */ + public function provider_auto_link() + { + return array( + array( + 'Hi there, my site is kohanaframework.org and you can email me at nobody@kohanaframework.org', + array('kohanaframework.org'), + ), + + array( + 'Hi my.domain.com@domain.com you came from', + FALSE, + array('my.domain.com@domain.com'), + ), + ); + } + + /** + * Tests Text::auto_link() + * + * @test + * @dataProvider provider_auto_link + */ + public function test_auto_link($text, $urls = array(), $emails = array()) + { + $linked_text = Text::auto_link($text); + + if ($urls === FALSE) + { + $this->assertNotContains('http://', $linked_text); + } + elseif (count($urls)) + { + foreach ($urls as $url) + { + // Assert that all the urls have been caught by text auto_link_urls() + $this->assertContains(Text::auto_link_urls($url), $linked_text); + } + } + + foreach ($emails as $email) + { + $this->assertContains('mailto:'.$email, $linked_text); + } + + } + +} diff --git a/system/tests/kohana/URLTest.php b/system/tests/kohana/URLTest.php new file mode 100644 index 0000000..ed5d9f1 --- /dev/null +++ b/system/tests/kohana/URLTest.php @@ -0,0 +1,279 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_URLTest extends Unittest_TestCase +{ + /** + * Default values for the environment, see setEnvironment + * @var array + */ + // @codingStandardsIgnoreStart + protected $environmentDefault = array( + 'Kohana::$base_url' => '/kohana/', + 'Kohana::$index_file'=> 'index.php', + 'HTTP_HOST' => 'example.com', + '_GET' => array(), + ); + // @codingStandardsIgnoreEnd + + /** + * Provides test data for test_base() + * + * @return array + */ + public function provider_base() + { + return array( + // $protocol, $index, $expected, $enviroment + + // Test with different combinations of parameters for max code coverage + array(NULL, FALSE, '/kohana/'), + array('http', FALSE, 'http://example.com/kohana/'), + array(NULL, TRUE, '/kohana/index.php/'), + array(NULL, TRUE, '/kohana/index.php/'), + array('http', TRUE, 'http://example.com/kohana/index.php/'), + array('https', TRUE, 'https://example.com/kohana/index.php/'), + array('ftp', TRUE, 'ftp://example.com/kohana/index.php/'), + + // Test for automatic protocol detection, protocol = TRUE + array(TRUE, TRUE, 'cli://example.com/kohana/index.php/', array('HTTPS' => FALSE, 'Request::$initial' => Request::factory('/')->protocol('cli'))), + + // Change base url' + array('https', FALSE, 'https://example.com/kohana/', array('Kohana::$base_url' => 'omglol://example.com/kohana/')), + + // Use port in base url, issue #3307 + array('http', FALSE, 'http://example.com:8080/', array('Kohana::$base_url' => 'example.com:8080/')), + + // Use protocol from base url if none specified + array(NULL, FALSE, 'http://www.example.com/', array('Kohana::$base_url' => 'http://www.example.com/')), + + // Use HTTP_HOST before SERVER_NAME + array('http', FALSE, 'http://example.com/kohana/', array('HTTP_HOST' => 'example.com', 'SERVER_NAME' => 'example.org')), + + // Use SERVER_NAME if HTTP_HOST DNX + array('http', FALSE, 'http://example.org/kohana/', array('HTTP_HOST' => NULL, 'SERVER_NAME' => 'example.org')), + ); + } + + /** + * Tests URL::base() + * + * @test + * @dataProvider provider_base + * @param boolean $protocol Parameter for Url::base() + * @param boolean $index Parameter for Url::base() + * @param string $expected Expected url + * @param array $enviroment Array of enviroment vars to change @see Kohana_URLTest::setEnvironment() + */ + public function test_base($protocol, $index, $expected, array $enviroment = array()) + { + $this->setEnvironment($enviroment); + + $this->assertSame( + $expected, + URL::base($protocol, $index) + ); + } + + /** + * Provides test data for test_site() + * + * @return array + */ + public function provider_site() + { + return array( + array('', NULL, '/kohana/index.php/'), + array('', 'http', 'http://example.com/kohana/index.php/'), + + array('my/site', NULL, '/kohana/index.php/my/site'), + array('my/site', 'http', 'http://example.com/kohana/index.php/my/site'), + + // @ticket #3110 + array('my/site/page:5', NULL, '/kohana/index.php/my/site/page:5'), + array('my/site/page:5', 'http', 'http://example.com/kohana/index.php/my/site/page:5'), + + array('my/site?var=asd&kohana=awesome', NULL, '/kohana/index.php/my/site?var=asd&kohana=awesome'), + array('my/site?var=asd&kohana=awesome', 'http', 'http://example.com/kohana/index.php/my/site?var=asd&kohana=awesome'), + + array('?kohana=awesome&life=good', NULL, '/kohana/index.php/?kohana=awesome&life=good'), + array('?kohana=awesome&life=good', 'http', 'http://example.com/kohana/index.php/?kohana=awesome&life=good'), + + array('?kohana=awesome&life=good#fact', NULL, '/kohana/index.php/?kohana=awesome&life=good#fact'), + array('?kohana=awesome&life=good#fact', 'http', 'http://example.com/kohana/index.php/?kohana=awesome&life=good#fact'), + + array('some/long/route/goes/here?kohana=awesome&life=good#fact', NULL, '/kohana/index.php/some/long/route/goes/here?kohana=awesome&life=good#fact'), + array('some/long/route/goes/here?kohana=awesome&life=good#fact', 'http', 'http://example.com/kohana/index.php/some/long/route/goes/here?kohana=awesome&life=good#fact'), + + array('/route/goes/here?kohana=awesome&life=good#fact', 'https', 'https://example.com/kohana/index.php/route/goes/here?kohana=awesome&life=good#fact'), + array('/route/goes/here?kohana=awesome&life=good#fact', 'ftp', 'ftp://example.com/kohana/index.php/route/goes/here?kohana=awesome&life=good#fact'), + ); + } + + /** + * Tests URL::site() + * + * @test + * @dataProvider provider_site + * @param string $uri URI to use + * @param boolean|string $protocol Protocol to use + * @param string $expected Expected result + * @param array $enviroment Array of enviroment vars to set + */ + public function test_site($uri, $protocol, $expected, array $enviroment = array()) + { + $this->setEnvironment($enviroment); + + $this->assertSame( + $expected, + URL::site($uri, $protocol) + ); + } + + /** + * Provides test data for test_site_url_encode_uri() + * See issue #2680 + * + * @return array + */ + public function provider_site_url_encode_uri() + { + $provider = array( + array('test', 'encode'), + array('test', 'éñçø∂ë∂'), + array('†é߆', 'encode'), + array('†é߆', 'éñçø∂ë∂', 'µåñ¥'), + ); + + foreach ($provider as $i => $params) + { + // Every non-ASCII character except for forward slash should be encoded... + $expected = implode('/', array_map('rawurlencode', $params)); + + // ... from a URI that is not encoded + $uri = implode('/', $params); + + $provider[$i] = array("/kohana/index.php/{$expected}", $uri); + } + + return $provider; + } + + /** + * Tests URL::site for proper URL encoding when working with non-ASCII characters. + * + * @test + * @dataProvider provider_site_url_encode_uri + */ + public function test_site_url_encode_uri($expected, $uri) + { + $this->assertSame($expected, URL::site($uri, FALSE)); + } + + /** + * Provides test data for test_title() + * @return array + */ + public function provider_title() + { + return array( + // Tests that.. + // Title is converted to lowercase + array('we-shall-not-be-moved', 'WE SHALL NOT BE MOVED', '-'), + // Excessive white space is removed and replaced with 1 char + array('thissssss-is-it', 'THISSSSSS IS IT ', '-'), + // separator is either - (dash) or _ (underscore) & others are converted to underscores + array('some-title', 'some title', '-'), + array('some_title', 'some title', '_'), + array('some!title', 'some title', '!'), + array('some:title', 'some title', ':'), + // Numbers are preserved + array('99-ways-to-beat-apple', '99 Ways to beat apple', '-'), + // ... with lots of spaces & caps + array('99_ways_to_beat_apple', '99 ways TO beat APPLE', '_'), + array('99-ways-to-beat-apple', '99 ways TO beat APPLE', '-'), + // Invalid characters are removed + array('each-gbp-is-now-worth-32-usd', 'Each GBP(£) is now worth 32 USD($)', '-'), + // ... inc. separator + array('is-it-reusable-or-re-usable', 'Is it reusable or re-usable?', '-'), + // Doing some crazy UTF8 tests + array('espana-wins', 'España-wins', '-', TRUE), + ); + } + + /** + * Tests URL::title() + * + * @test + * @dataProvider provider_title + * @param string $title Input to convert + * @param string $separator Seperate to replace invalid characters with + * @param string $expected Expected result + */ + public function test_title($expected, $title, $separator, $ascii_only = FALSE) + { + $this->assertSame( + $expected, + URL::title($title, $separator, $ascii_only) + ); + } + + /** + * Provides test data for URL::query() + * @return array + */ + public function provider_query() + { + return array( + array(array(), '', NULL), + array(array('_GET' => array('test' => 'data')), '?test=data', NULL), + array(array(), '?test=data', array('test' => 'data')), + array(array('_GET' => array('more' => 'data')), '?more=data&test=data', array('test' => 'data')), + array(array('_GET' => array('sort' => 'down')), '?test=data', array('test' => 'data'), FALSE), + + // http://dev.kohanaframework.org/issues/3362 + array(array(), '', array('key' => NULL)), + array(array(), '?key=0', array('key' => FALSE)), + array(array(), '?key=1', array('key' => TRUE)), + array(array('_GET' => array('sort' => 'down')), '?sort=down&key=1', array('key' => TRUE)), + array(array('_GET' => array('sort' => 'down')), '?sort=down&key=0', array('key' => FALSE)), + + // @issue 4240 + array(array('_GET' => array('foo' => array('a' => 100))), '?foo%5Ba%5D=100&foo%5Bb%5D=bar', array('foo' => array('b' => 'bar'))), + array(array('_GET' => array('a' => 'a')), '?a=b', array('a' => 'b')), + ); + } + + /** + * Tests URL::query() + * + * @test + * @dataProvider provider_query + * @param array $enviroment Set environment + * @param string $expected Expected result + * @param array $params Query string + * @param boolean $use_get Combine with GET parameters + */ + public function test_query($enviroment, $expected, $params, $use_get = TRUE) + { + $this->setEnvironment($enviroment); + + $this->assertSame( + $expected, + URL::query($params, $use_get) + ); + } +} diff --git a/system/tests/kohana/UTF8Test.php b/system/tests/kohana/UTF8Test.php new file mode 100644 index 0000000..fec2d51 --- /dev/null +++ b/system/tests/kohana/UTF8Test.php @@ -0,0 +1,631 @@ +assertSame($expected, UTF8::clean($input)); + } + + /** + * Provides test data for test_is_ascii() + */ + public function provider_is_ascii() + { + return array( + array("\0", TRUE), + array("\$eno\r", TRUE), + array('Señor', FALSE), + array(array('Se', 'nor'), TRUE), + array(array('Se', 'ñor'), FALSE), + ); + } + + /** + * Tests UTF8::is_ascii + * + * @test + * @dataProvider provider_is_ascii + */ + public function test_is_ascii($input, $expected) + { + $this->assertSame($expected, UTF8::is_ascii($input)); + } + + /** + * Provides test data for test_strip_ascii_ctrl() + */ + public function provider_strip_ascii_ctrl() + { + return array( + array("\0", ''), + array("→foo\021", '→foo'), + array("\x7Fbar", 'bar'), + array("\xFF", "\xFF"), + array("\x41", 'A'), + ); + } + + /** + * Tests UTF8::strip_ascii_ctrl + * + * @test + * @dataProvider provider_strip_ascii_ctrl + */ + public function test_strip_ascii_ctrl($input, $expected) + { + $this->assertSame($expected, UTF8::strip_ascii_ctrl($input)); + } + + /** + * Provides test data for test_strip_non_ascii() + */ + public function provider_strip_non_ascii() + { + return array( + array("\0\021\x7F", "\0\021\x7F"), + array('I ♥ cocoñùт', 'I coco'), + ); + } + + /** + * Tests UTF8::strip_non_ascii + * + * @test + * @dataProvider provider_strip_non_ascii + */ + public function test_strip_non_ascii($input, $expected) + { + $this->assertSame($expected, UTF8::strip_non_ascii($input)); + } + + /** + * Provides test data for test_transliterate_to_ascii() + */ + public function provider_transliterate_to_ascii() + { + return array( + array('Cocoñùт', -1, 'Coconuт'), + array('COCOÑÙТ', -1, 'COCOÑÙТ'), + array('Cocoñùт', 0, 'Coconuт'), + array('COCOÑÙТ', 0, 'COCONUТ'), + array('Cocoñùт', 1, 'Cocoñùт'), + array('COCOÑÙТ', 1, 'COCONUТ'), + ); + } + + /** + * Tests UTF8::transliterate_to_ascii + * + * @test + * @dataProvider provider_transliterate_to_ascii + */ + public function test_transliterate_to_ascii($input, $case, $expected) + { + $this->assertSame($expected, UTF8::transliterate_to_ascii($input, $case)); + } + + /** + * Provides test data for test_strlen() + */ + public function provider_strlen() + { + return array( + array('Cocoñùт', 7), + array('Coconut', 7), + ); + } + + /** + * Tests UTF8::strlen + * + * @test + * @dataProvider provider_strlen + */ + public function test_strlen($input, $expected) + { + $this->assertSame($expected, UTF8::strlen($input)); + } + + /** + * Provides test data for test_strpos() + */ + public function provider_strpos() + { + return array( + array('Cocoñùт', 'o', 0, 1), + array('Cocoñùт', 'ñ', 1, 4), + ); + } + + /** + * Tests UTF8::strpos + * + * @test + * @dataProvider provider_strpos + */ + public function test_strpos($input, $str, $offset, $expected) + { + $this->assertSame($expected, UTF8::strpos($input, $str, $offset)); + } + + /** + * Provides test data for test_strrpos() + */ + public function provider_strrpos() + { + return array( + array('Cocoñùт', 'o', 0, 3), + array('Cocoñùт', 'ñ', 2, 4), + ); + } + + /** + * Tests UTF8::strrpos + * + * @test + * @dataProvider provider_strrpos + */ + public function test_strrpos($input, $str, $offset, $expected) + { + $this->assertSame($expected, UTF8::strrpos($input, $str, $offset)); + } + + /** + * Provides test data for test_substr() + */ + public function provider_substr() + { + return array( + array('Cocoñùт', 3, 2, 'oñ'), + array('Cocoñùт', 3, 9, 'oñùт'), + array('Cocoñùт', 3, NULL, 'oñùт'), + array('Cocoñùт', 3, -2, 'oñ'), + ); + } + + /** + * Tests UTF8::substr + * + * @test + * @dataProvider provider_substr + */ + public function test_substr($input, $offset, $length, $expected) + { + $this->assertSame($expected, UTF8::substr($input, $offset, $length)); + } + + /** + * Provides test data for test_substr_replace() + */ + public function provider_substr_replace() + { + return array( + array('Cocoñùт', 'šš', 3, 2, 'Cocššùт'), + array('Cocoñùт', 'šš', 3, 9, 'Cocšš'), + ); + } + + /** + * Tests UTF8::substr_replace + * + * @test + * @dataProvider provider_substr_replace + */ + public function test_substr_replace($input, $replacement, $offset, $length, $expected) + { + $this->assertSame($expected, UTF8::substr_replace($input, $replacement, $offset, $length)); + } + + /** + * Provides test data for test_strtolower() + */ + public function provider_strtolower() + { + return array( + array('COCOÑÙТ', 'cocoñùт'), + array('JÄGER', 'jäger'), + ); + } + + /** + * Tests UTF8::strtolower + * + * @test + * @dataProvider provider_strtolower + */ + public function test_strtolower($input, $expected) + { + $this->assertSame($expected, UTF8::strtolower($input)); + } + + /** + * Provides test data for test_strtoupper() + */ + public function provider_strtoupper() + { + return array( + array('Cocoñùт', 'COCOÑÙТ'), + array('jäger', 'JÄGER'), + ); + } + + /** + * Tests UTF8::strtoupper + * + * @test + * @dataProvider provider_strtoupper + */ + public function test_strtoupper($input, $expected) + { + $this->assertSame($expected, UTF8::strtoupper($input)); + } + + /** + * Provides test data for test_ucfirst() + */ + public function provider_ucfirst() + { + return array( + array('ñùт', 'Ñùт'), + ); + } + + /** + * Tests UTF8::ucfirst + * + * @test + * @dataProvider provider_ucfirst + */ + public function test_ucfirst($input, $expected) + { + $this->assertSame($expected, UTF8::ucfirst($input)); + } + + /** + * Provides test data for test_strip_non_ascii() + */ + public function provider_ucwords() + { + return array( + array('ExAmple', 'ExAmple'), + array('i ♥ Cocoñùт', 'I ♥ Cocoñùт'), + ); + } + + /** + * Tests UTF8::ucwords + * + * @test + * @dataProvider provider_ucwords + */ + public function test_ucwords($input, $expected) + { + $this->assertSame($expected, UTF8::ucwords($input)); + } + + /** + * Provides test data for test_strcasecmp() + */ + public function provider_strcasecmp() + { + return array( + array('Cocoñùт', 'Cocoñùт', 0), + array('Čau', 'Čauo', -1), + array('Čau', 'Ča', 1), + array('Cocoñùт', 'Cocoñ', 4), + array('Cocoñùт', 'Coco', 6), + ); + } + + /** + * Tests UTF8::strcasecmp + * + * @test + * @dataProvider provider_strcasecmp + */ + public function test_strcasecmp($input, $input2, $expected) + { + $this->assertSame($expected, UTF8::strcasecmp($input, $input2)); + } + + /** + * Provides test data for test_str_ireplace() + */ + public function provider_str_ireplace() + { + return array( + array('т', 't', 'cocoñuт', 'cocoñut'), + array('Ñ', 'N', 'cocoñuт', 'cocoNuт'), + array(array('т', 'Ñ', 'k' => 'k'), array('t', 'N', 'K'), array('cocoñuт'), array('cocoNut')), + array(array('ñ'), 'n', 'cocoñuт', 'coconuт'), + ); + } + + /** + * Tests UTF8::str_ireplace + * + * @test + * @dataProvider provider_str_ireplace + */ + public function test_str_ireplace($search, $replace, $subject, $expected) + { + $this->assertSame($expected, UTF8::str_ireplace($search, $replace, $subject)); + } + + /** + * Provides test data for test_stristr() + */ + public function provider_stristr() + { + return array( + array('Cocoñùт', 'oñ', 'oñùт'), + array('Cocoñùт', 'o', 'ocoñùт'), + array('Cocoñùт', 'k', FALSE), + ); + } + + /** + * Tests UTF8::stristr + * + * @test + * @dataProvider provider_stristr + */ + public function test_stristr($input, $input2, $expected) + { + $this->assertSame($expected, UTF8::stristr($input, $input2)); + } + + /** + * Provides test data for test_strspn() + */ + public function provider_strspn() + { + return array( + array("foo", "o", 1, 2, 2), + array('Cocoñùт', 'oñ', NULL, NULL, 1), + array('Cocoñùт', 'oñ', 2, 4, 1), + array('Cocoñùт', 'šš', 3, 9, 4), + ); + } + + /** + * Tests UTF8::strspn + * + * @test + * @dataProvider provider_strspn + */ + public function test_strspn($input, $mask, $offset, $length, $expected) + { + $this->assertSame($expected, UTF8::strspn($input, $mask, $offset, $length)); + } + + /** + * Provides test data for test_strcspn() + */ + public function provider_strcspn() + { + return array( + array('Cocoñùт', 'oñ', NULL, NULL, 1), + array('Cocoñùт', 'oñ', 2, 4, 1), + array('Cocoñùт', 'šš', 3, 9, 4), + ); + } + + /** + * Tests UTF8::strcspn + * + * @test + * @dataProvider provider_strcspn + */ + public function test_strcspn($input, $mask, $offset, $length, $expected) + { + $this->assertSame($expected, UTF8::strcspn($input, $mask, $offset, $length)); + } + + /** + * Provides test data for test_str_pad() + */ + public function provider_str_pad() + { + return array( + array('Cocoñùт', 10, 'š', STR_PAD_RIGHT, 'Cocoñùтššš'), + array('Cocoñùт', 10, 'š', STR_PAD_LEFT, 'šššCocoñùт'), + array('Cocoñùт', 10, 'š', STR_PAD_BOTH, 'šCocoñùтšš'), + ); + } + + /** + * Tests UTF8::str_pad + * + * @test + * @dataProvider provider_str_pad + */ + public function test_str_pad($input, $length, $pad, $type, $expected) + { + $this->assertSame($expected, UTF8::str_pad($input, $length, $pad, $type)); + } + + /** + * Tests UTF8::str_pad error + * + * @test + * @expectedException UTF8_Exception + */ + public function test_str_pad_error() + { + UTF8::str_pad('Cocoñùт', 10, 'š', 15, 'šCocoñùтšš'); + } + + /** + * Provides test data for test_str_split() + */ + public function provider_str_split() + { + return array( + array('Bár', 1, array('B', 'á', 'r')), + array('Cocoñùт', 2, array('Co', 'co', 'ñù', 'т')), + array('Cocoñùт', 3, array('Coc', 'oñù', 'т')), + ); + } + + /** + * Tests UTF8::str_split + * + * @test + * @dataProvider provider_str_split + */ + public function test_str_split($input, $split_length, $expected) + { + $this->assertSame($expected, UTF8::str_split($input, $split_length)); + } + + /** + * Provides test data for test_strrev() + */ + public function provider_strrev() + { + return array( + array('Cocoñùт', 'тùñocoC'), + ); + } + + /** + * Tests UTF8::strrev + * + * @test + * @dataProvider provider_strrev + */ + public function test_strrev($input, $expected) + { + $this->assertSame($expected, UTF8::strrev($input)); + } + + /** + * Provides test data for test_trim() + */ + public function provider_trim() + { + return array( + array(' bar ', NULL, 'bar'), + array('bar', 'b', 'ar'), + array('barb', 'b', 'ar'), + ); + } + + /** + * Tests UTF8::trim + * + * @test + * @dataProvider provider_trim + */ + public function test_trim($input, $input2, $expected) + { + $this->assertSame($expected, UTF8::trim($input, $input2)); + } + + /** + * Provides test data for test_ltrim() + */ + public function provider_ltrim() + { + return array( + array(' bar ', NULL, 'bar '), + array('bar', 'b', 'ar'), + array('barb', 'b', 'arb'), + array('ñùт', 'ñ', 'ùт'), + ); + } + + /** + * Tests UTF8::ltrim + * + * @test + * @dataProvider provider_ltrim + */ + public function test_ltrim($input, $charlist, $expected) + { + $this->assertSame($expected, UTF8::ltrim($input, $charlist)); + } + + /** + * Provides test data for test_rtrim() + */ + public function provider_rtrim() + { + return array( + array(' bar ', NULL, ' bar'), + array('bar', 'b', 'bar'), + array('barb', 'b', 'bar'), + array('Cocoñùт', 'т', 'Cocoñù'), + ); + } + + /** + * Tests UTF8::rtrim + * + * @test + * @dataProvider provider_rtrim + */ + public function test_rtrim($input, $input2, $expected) + { + $this->assertSame($expected, UTF8::rtrim($input, $input2)); + } + + /** + * Provides test data for test_ord() + */ + public function provider_ord() + { + return array( + array('f', 102), + array('ñ', 241), + array('Ñ', 209), + ); + } + + /** + * Tests UTF8::ord + * + * @test + * @dataProvider provider_ord + */ + public function test_ord($input, $expected) + { + $this->assertSame($expected, UTF8::ord($input)); + } +} diff --git a/system/tests/kohana/UploadTest.php b/system/tests/kohana/UploadTest.php new file mode 100644 index 0000000..026ba8a --- /dev/null +++ b/system/tests/kohana/UploadTest.php @@ -0,0 +1,225 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_UploadTest extends Unittest_TestCase +{ + /** + * Provides test data for test_size() + * + * @return array + */ + public function provider_size() + { + return array( + // $field, $bytes, $environment, $expected + array( + 'unit_test', + 5, + array('_FILES' => array('unit_test' => array('error' => UPLOAD_ERR_INI_SIZE))), + FALSE + ), + array( + 'unit_test', + 5, + array('_FILES' => array('unit_test' => array('error' => UPLOAD_ERR_NO_FILE))), + TRUE + ), + array( + 'unit_test', + '6K', + array('_FILES' => array( + 'unit_test' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ) + ), + TRUE + ), + array( + 'unit_test', + '1B', + array('_FILES' => array( + 'unit_test' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ) + ), + FALSE + ), + ); + } + + /** + * Tests Upload::size + * + * @test + * @dataProvider provider_size + * @covers upload::size + * @param string $field the files field to test + * @param string $bytes valid bite size + * @param array $environment set the $_FILES array + * @param bool $expected what to expect + */ + public function test_size($field, $bytes, $environment, $expected) + { + $this->setEnvironment($environment); + + $this->assertSame($expected, Upload::size($_FILES[$field], $bytes)); + } + + /** + * size() should throw an exception of the supplied max size is invalid + * + * @test + * @covers upload::size + * @expectedException Kohana_Exception + */ + public function test_size_throws_exception_for_invalid_size() + { + $this->setEnvironment(array( + '_FILES' => array( + 'unit_test' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ) + )); + + Upload::size($_FILES['unit_test'], '1DooDah'); + } + + /** + * Provides test data for test_vali() + * + * @test + * @return array + */ + public function provider_valid() + { + return array( + array( + TRUE, + array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ), + array( + FALSE, + array( + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ), + array( + FALSE, + array( + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ), + array( + FALSE, + array( + 'name' => 'Unit_Test File', + 'error' => UPLOAD_ERR_OK, + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ), + array( + FALSE, + array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ), + array( + FALSE, + array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'Unit_Test File', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + ) + ), + + ); + } + + /** + * Test Upload::valid + * + * @test + * @dataProvider provider_valid + * @covers Upload::valid + */ + public function test_valid($expected, $file) + { + $this->setEnvironment(array( + '_FILES' => array( + 'unit_test' => $file, + ), + )); + + $this->assertSame($expected, Upload::valid($_FILES['unit_test'])); + } + + /** + * Tests Upload::type + * + * @test + * @covers Upload::type + */ + public function test_type() + { + $this->setEnvironment(array( + '_FILES' => array( + 'unit_test' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'github.png', + 'type' => 'image/png', + 'tmp_name' => Kohana::find_file('tests', 'test_data/github', 'png'), + 'size' => filesize(Kohana::find_file('tests', 'test_data/github', 'png')), + ) + ) + )); + + $this->assertTrue(Upload::type($_FILES['unit_test'], array('jpg', 'png', 'gif'))); + + $this->assertFalse(Upload::type($_FILES['unit_test'], array('docx'))); + } +} diff --git a/system/tests/kohana/ValidTest.php b/system/tests/kohana/ValidTest.php new file mode 100644 index 0000000..929e0f0 --- /dev/null +++ b/system/tests/kohana/ValidTest.php @@ -0,0 +1,985 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_ValidTest extends Unittest_TestCase +{ + + /** + * Provides test data for test_alpha() + * @return array + */ + public function provider_alpha() + { + return array( + array('asdavafaiwnoabwiubafpowf', TRUE), + array('!aidhfawiodb', FALSE), + array('51535oniubawdawd78', FALSE), + array('!"£$(G$W£(HFW£F(HQ)"n', FALSE), + // UTF-8 tests + array('あいうえお', TRUE, TRUE), + array('¥', FALSE, TRUE), + // Empty test + array('', FALSE, FALSE), + array(NULL, FALSE, FALSE), + array(FALSE, FALSE, FALSE), + ); + } + + /** + * Tests Valid::alpha() + * + * Checks whether a string consists of alphabetical characters only. + * + * @test + * @dataProvider provider_alpha + * @param string $string + * @param boolean $expected + */ + public function test_alpha($string, $expected, $utf8 = FALSE) + { + $this->assertSame( + $expected, + Valid::alpha($string, $utf8) + ); + } + + /* + * Provides test data for test_alpha_numeric + */ + public function provide_alpha_numeric() + { + return array( + array('abcd1234', TRUE), + array('abcd', TRUE), + array('1234', TRUE), + array('abc123&^/-', FALSE), + // UTF-8 tests + array('あいうえお', TRUE, TRUE), + array('零一二三四五', TRUE, TRUE), + array('あい四五£^£^', FALSE, TRUE), + // Empty test + array('', FALSE, FALSE), + array(NULL, FALSE, FALSE), + array(FALSE, FALSE, FALSE), + ); + } + + /** + * Tests Valid::alpha_numeric() + * + * Checks whether a string consists of alphabetical characters and numbers only. + * + * @test + * @dataProvider provide_alpha_numeric + * @param string $input The string to test + * @param boolean $expected Is $input valid + */ + public function test_alpha_numeric($input, $expected, $utf8 = FALSE) + { + $this->assertSame( + $expected, + Valid::alpha_numeric($input, $utf8) + ); + } + + /** + * Provides test data for test_alpha_dash + */ + public function provider_alpha_dash() + { + return array( + array('abcdef', TRUE), + array('12345', TRUE), + array('abcd1234', TRUE), + array('abcd1234-', TRUE), + array('abc123&^/-', FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::alpha_dash() + * + * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only. + * + * @test + * @dataProvider provider_alpha_dash + * @param string $input The string to test + * @param boolean $contains_utf8 Does the string contain utf8 specific characters + * @param boolean $expected Is $input valid? + */ + public function test_alpha_dash($input, $expected, $contains_utf8 = FALSE) + { + if ( ! $contains_utf8) + { + $this->assertSame( + $expected, + Valid::alpha_dash($input) + ); + } + + $this->assertSame( + $expected, + Valid::alpha_dash($input, TRUE) + ); + } + + /** + * DataProvider for the valid::date() test + */ + public function provider_date() + { + return array( + array('now',TRUE), + array('10 September 2010',TRUE), + array('+1 day',TRUE), + array('+1 week',TRUE), + array('+1 week 2 days 4 hours 2 seconds',TRUE), + array('next Thursday',TRUE), + array('last Monday',TRUE), + + array('blarg',FALSE), + array('in the year 2000',FALSE), + array('324824',FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::date() + * + * @test + * @dataProvider provider_date + * @param string $date The date to validate + * @param integer $expected + */ + public function test_date($date, $expected) + { + $this->assertSame( + $expected, + Valid::date($date, $expected) + ); + } + + /** + * DataProvider for the valid::decimal() test + */ + public function provider_decimal() + { + return array( + // Empty test + array('', 2, NULL, FALSE), + array(NULL, 2, NULL, FALSE), + array(FALSE, 2, NULL, FALSE), + array('45.1664', 3, NULL, FALSE), + array('45.1664', 4, NULL, TRUE), + array('45.1664', 4, 2, TRUE), + array('-45.1664', 4, NULL, TRUE), + array('+45.1664', 4, NULL, TRUE), + array('-45.1664', 3, NULL, FALSE), + ); + } + + /** + * Tests Valid::decimal() + * + * @test + * @dataProvider provider_decimal + * @param string $decimal The decimal to validate + * @param integer $places The number of places to check to + * @param integer $digits The number of digits preceding the point to check + * @param boolean $expected Whether $decimal conforms to $places AND $digits + */ + public function test_decimal($decimal, $places, $digits, $expected) + { + $this->assertSame( + $expected, + Valid::decimal($decimal, $places, $digits), + 'Decimal: "'.$decimal.'" to '.$places.' places and '.$digits.' digits (preceeding period)' + ); + } + + /** + * Provides test data for test_digit + * @return array + */ + public function provider_digit() + { + return array( + array('12345', TRUE), + array('10.5', FALSE), + array('abcde', FALSE), + array('abcd1234', FALSE), + array('-5', FALSE), + array(-5, FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::digit() + * + * @test + * @dataProvider provider_digit + * @param mixed $input Input to validate + * @param boolean $expected Is $input valid + */ + public function test_digit($input, $expected, $contains_utf8 = FALSE) + { + if ( ! $contains_utf8) + { + $this->assertSame( + $expected, + Valid::digit($input) + ); + } + + $this->assertSame( + $expected, + Valid::digit($input, TRUE) + ); + + } + + /** + * DataProvider for the valid::color() test + */ + public function provider_color() + { + return array( + array('#000000', TRUE), + array('#GGGGGG', FALSE), + array('#AbCdEf', TRUE), + array('#000', TRUE), + array('#abc', TRUE), + array('#DEF', TRUE), + array('000000', TRUE), + array('GGGGGG', FALSE), + array('AbCdEf', TRUE), + array('000', TRUE), + array('DEF', TRUE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::color() + * + * @test + * @dataProvider provider_color + * @param string $color The color to test + * @param boolean $expected Is $color valid + */ + public function test_color($color, $expected) + { + $this->assertSame( + $expected, + Valid::color($color) + ); + } + + /** + * Provides test data for test_credit_card() + */ + public function provider_credit_card() + { + return array( + array('4222222222222', 'visa', TRUE), + array('4012888888881881', 'visa', TRUE), + array('4012888888881881', NULL, TRUE), + array('4012888888881881', array('mastercard', 'visa'), TRUE), + array('4012888888881881', array('discover', 'mastercard'), FALSE), + array('4012888888881881', 'mastercard', FALSE), + array('5105105105105100', 'mastercard', TRUE), + array('6011111111111117', 'discover', TRUE), + array('6011111111111117', 'visa', FALSE), + // Empty test + array('', NULL, FALSE), + array(NULL, NULL, FALSE), + array(FALSE, NULL, FALSE), + ); + } + + /** + * Tests Valid::credit_card() + * + * @test + * @covers Valid::credit_card + * @dataProvider provider_credit_card() + * @param string $number Credit card number + * @param string $type Credit card type + * @param boolean $expected + */ + public function test_credit_card($number, $type, $expected) + { + $this->assertSame( + $expected, + Valid::credit_card($number, $type) + ); + } + + /** + * Provides test data for test_credit_card() + */ + public function provider_luhn() + { + return array( + array('4222222222222', TRUE), + array('4012888888881881', TRUE), + array('5105105105105100', TRUE), + array('6011111111111117', TRUE), + array('60111111111111.7', FALSE), + array('6011111111111117X', FALSE), + array('6011111111111117 ', FALSE), + array('WORD ', FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::luhn() + * + * @test + * @covers Valid::luhn + * @dataProvider provider_luhn() + * @param string $number Credit card number + * @param boolean $expected + */ + public function test_luhn($number, $expected) + { + $this->assertSame( + $expected, + Valid::luhn($number) + ); + } + + /** + * Provides test data for test_email() + * + * @return array + */ + public function provider_email() + { + return array( + array('foo', TRUE, FALSE), + array('foo', FALSE, FALSE), + + array('foo@bar', TRUE, TRUE), + // RFC is less strict than the normal regex, presumably to allow + // admin@localhost, therefore we IGNORE IT!!! + array('foo@bar', FALSE, FALSE), + array('foo@bar.com', FALSE, TRUE), + array('foo@barcom:80', FALSE, FALSE), + array('foo@bar.sub.com', FALSE, TRUE), + array('foo+asd@bar.sub.com', FALSE, TRUE), + array('foo.asd@bar.sub.com', FALSE, TRUE), + // RFC says 254 length max #4011 + array(Text::random(NULL, 200).'@'.Text::random(NULL, 50).'.com', FALSE, FALSE), + // Empty test + array('', TRUE, FALSE), + array(NULL, TRUE, FALSE), + array(FALSE, TRUE, FALSE), + ); + } + + /** + * Tests Valid::email() + * + * Check an email address for correct format. + * + * @test + * @dataProvider provider_email + * @param string $email Address to check + * @param boolean $strict Use strict settings + * @param boolean $correct Is $email address valid? + */ + public function test_email($email, $strict, $correct) + { + $this->assertSame( + $correct, + Valid::email($email, $strict) + ); + } + + /** + * Returns test data for test_email_domain() + * + * @return array + */ + public function provider_email_domain() + { + return array( + array('google.com', TRUE), + // Don't anybody dare register this... + array('DAWOMAWIDAIWNDAIWNHDAWIHDAIWHDAIWOHDAIOHDAIWHD.com', FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::email_domain() + * + * Validate the domain of an email address by checking if the domain has a + * valid MX record. + * + * Test skips on windows + * + * @test + * @dataProvider provider_email_domain + * @param string $email Email domain to check + * @param boolean $correct Is it correct? + */ + public function test_email_domain($email, $correct) + { + if ( ! $this->hasInternet()) + { + $this->markTestSkipped('An internet connection is required for this test'); + } + + if ( ! Kohana::$is_windows OR version_compare(PHP_VERSION, '5.3.0', '>=')) + { + $this->assertSame( + $correct, + Valid::email_domain($email) + ); + } + else + { + $this->markTestSkipped('checkdnsrr() was not added on windows until PHP 5.3'); + } + } + + /** + * Provides data for test_exact_length() + * + * @return array + */ + public function provider_exact_length() + { + return array( + array('somestring', 10, TRUE), + array('somestring', 11, FALSE), + array('anotherstring', 13, TRUE), + // Empty test + array('', 10, FALSE), + array(NULL, 10, FALSE), + array(FALSE, 10, FALSE), + // Test array of allowed lengths + array('somestring', array(1, 3, 5, 7, 9, 10), TRUE), + array('somestring', array(1, 3, 5, 7, 9), FALSE), + ); + } + + /** + * + * Tests Valid::exact_length() + * + * Checks that a field is exactly the right length. + * + * @test + * @dataProvider provider_exact_length + * @param string $string The string to length check + * @param integer $length The length of the string + * @param boolean $correct Is $length the actual length of the string? + * @return bool + */ + public function test_exact_length($string, $length, $correct) + { + return $this->assertSame( + $correct, + Valid::exact_length($string, $length), + 'Reported string length is not correct' + ); + } + + /** + * Provides data for test_equals() + * + * @return array + */ + public function provider_equals() + { + return array( + array('foo', 'foo', TRUE), + array('1', '1', TRUE), + array(1, '1', FALSE), + array('011', 011, FALSE), + // Empty test + array('', 123, FALSE), + array(NULL, 123, FALSE), + array(FALSE, 123, FALSE), + ); + } + + /** + * Tests Valid::equals() + * + * @test + * @dataProvider provider_equals + * @param string $string value to check + * @param integer $required required value + * @param boolean $correct is $string the same as $required? + * @return boolean + */ + public function test_equals($string, $required, $correct) + { + return $this->assertSame( + $correct, + Valid::equals($string, $required), + 'Values are not equal' + ); + } + + /** + * DataProvider for the valid::ip() test + * @return array + */ + public function provider_ip() + { + return array( + array('75.125.175.50', FALSE, TRUE), + // PHP 5.3.6 fixed a bug that allowed 127.0.0.1 as a public ip: http://bugs.php.net/53150 + array('127.0.0.1', FALSE, version_compare(PHP_VERSION, '5.3.6', '<')), + array('256.257.258.259', FALSE, FALSE), + array('255.255.255.255', FALSE, FALSE), + array('192.168.0.1', FALSE, FALSE), + array('192.168.0.1', TRUE, TRUE), + // Empty test + array('', TRUE, FALSE), + array(NULL, TRUE, FALSE), + array(FALSE, TRUE, FALSE), + ); + } + + /** + * Tests Valid::ip() + * + * @test + * @dataProvider provider_ip + * @param string $input_ip + * @param boolean $allow_private + * @param boolean $expected_result + */ + public function test_ip($input_ip, $allow_private, $expected_result) + { + $this->assertEquals( + $expected_result, + Valid::ip($input_ip, $allow_private) + ); + } + + /** + * Returns test data for test_max_length() + * + * @return array + */ + public function provider_max_length() + { + return array( + // Border line + array('some', 4, TRUE), + // Exceeds + array('KOHANARULLLES', 2, FALSE), + // Under + array('CakeSucks', 10, TRUE), + // Empty test + array('', -10, FALSE), + array(NULL, -10, FALSE), + array(FALSE, -10, FALSE), + ); + } + + /** + * Tests Valid::max_length() + * + * Checks that a field is short enough. + * + * @test + * @dataProvider provider_max_length + * @param string $string String to test + * @param integer $maxlength Max length for this string + * @param boolean $correct Is $string <= $maxlength + */ + public function test_max_length($string, $maxlength, $correct) + { + $this->assertSame( + $correct, + Valid::max_length($string, $maxlength) + ); + } + + /** + * Returns test data for test_min_length() + * + * @return array + */ + public function provider_min_length() + { + return array( + array('This is obviously long enough', 10, TRUE), + array('This is not', 101, FALSE), + array('This is on the borderline', 25, TRUE), + // Empty test + array('', 10, FALSE), + array(NULL, 10, FALSE), + array(FALSE, 10, FALSE), + ); + } + + /** + * Tests Valid::min_length() + * + * Checks that a field is long enough. + * + * @test + * @dataProvider provider_min_length + * @param string $string String to compare + * @param integer $minlength The minimum allowed length + * @param boolean $correct Is $string 's length >= $minlength + */ + public function test_min_length($string, $minlength, $correct) + { + $this->assertSame( + $correct, + Valid::min_length($string, $minlength) + ); + } + + /** + * Returns test data for test_not_empty() + * + * @return array + */ + public function provider_not_empty() + { + // Create a blank arrayObject + $ao = new ArrayObject; + + // arrayObject with value + $ao1 = new ArrayObject; + $ao1['test'] = 'value'; + + return array( + array(array(), FALSE), + array(NULL, FALSE), + array('', FALSE), + array($ao, FALSE), + array($ao1, TRUE), + array(array(NULL), TRUE), + array(0, TRUE), + array('0', TRUE), + array('Something', TRUE), + ); + } + + /** + * Tests Valid::not_empty() + * + * Checks if a field is not empty. + * + * @test + * @dataProvider provider_not_empty + * @param mixed $value Value to check + * @param boolean $empty Is the value really empty? + */ + public function test_not_empty($value, $empty) + { + return $this->assertSame( + $empty, + Valid::not_empty($value) + ); + } + + /** + * DataProvider for the Valid::numeric() test + */ + public function provider_numeric() + { + return array( + array(12345, TRUE), + array(123.45, TRUE), + array('12345', TRUE), + array('10.5', TRUE), + array('-10.5', TRUE), + array('10.5a', FALSE), + // @issue 3240 + array(.4, TRUE), + array(-.4, TRUE), + array(4., TRUE), + array(-4., TRUE), + array('.5', TRUE), + array('-.5', TRUE), + array('5.', TRUE), + array('-5.', TRUE), + array('.', FALSE), + array('1.2.3', FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + } + + /** + * Tests Valid::numeric() + * + * @test + * @dataProvider provider_numeric + * @param string $input Input to test + * @param boolean $expected Whether or not $input is numeric + */ + public function test_numeric($input, $expected) + { + $this->assertSame( + $expected, + Valid::numeric($input) + ); + } + + /** + * Provides test data for test_phone() + * @return array + */ + public function provider_phone() + { + return array( + array('0163634840', NULL, TRUE), + array('+27173634840', NULL, TRUE), + array('123578', NULL, FALSE), + // Some uk numbers + array('01234456778', NULL, TRUE), + array('+0441234456778', NULL, FALSE), + // Google UK case you're interested + array('+44 20-7031-3000', array(12), TRUE), + // BT Corporate + array('020 7356 5000', NULL, TRUE), + // Empty test + array('', NULL, FALSE), + array(NULL, NULL, FALSE), + array(FALSE, NULL, FALSE), + ); + } + + /** + * Tests Valid::phone() + * + * @test + * @dataProvider provider_phone + * @param string $phone Phone number to test + * @param boolean $expected Is $phone valid + */ + public function test_phone($phone, $lengths, $expected) + { + $this->assertSame( + $expected, + Valid::phone($phone, $lengths) + ); + } + + /** + * DataProvider for the valid::regex() test + */ + public function provider_regex() + { + return array( + array('hello world', '/[a-zA-Z\s]++/', TRUE), + array('123456789', '/[0-9]++/', TRUE), + array('£$%£%', '/[abc]/', FALSE), + array('Good evening', '/hello/', FALSE), + // Empty test + array('', '/hello/', FALSE), + array(NULL, '/hello/', FALSE), + array(FALSE, '/hello/', FALSE), + ); + } + + /** + * Tests Valid::range() + * + * Tests if a number is within a range. + * + * @test + * @dataProvider provider_regex + * @param string $value Value to test against + * @param string $regex Valid pcre regular expression + * @param bool $expected Does the value match the expression? + */ + public function test_regex($value, $regex, $expected) + { + $this->AssertSame( + $expected, + Valid::regex($value, $regex) + ); + } + + /** + * DataProvider for the valid::range() test + */ + public function provider_range() + { + return array( + array(1, 0, 2, NULL, TRUE), + array(-1, -5, 0, NULL, TRUE), + array(-1, 0, 1, NULL, FALSE), + array(1, 0, 0, NULL, FALSE), + array(2147483647, 0, 200000000000000, NULL, TRUE), + array(-2147483647, -2147483655, 2147483645, NULL, TRUE), + // #4043 + array(2, 0, 10, 2, TRUE), + array(3, 0, 10, 2, FALSE), + // Empty test + array('', 5, 10, NULL, FALSE), + array(NULL, 5, 10, NULL, FALSE), + array(FALSE, 5, 10, NULL, FALSE), + ); + } + + /** + * Tests Valid::range() + * + * Tests if a number is within a range. + * + * @test + * @dataProvider provider_range + * @param integer $number Number to test + * @param integer $min Lower bound + * @param integer $max Upper bound + * @param boolean $expected Is Number within the bounds of $min && $max + */ + public function test_range($number, $min, $max, $step, $expected) + { + $this->AssertSame( + $expected, + Valid::range($number, $min, $max, $step) + ); + } + + /** + * Provides test data for test_url() + * + * @return array + */ + public function provider_url() + { + $data = array( + array('http://google.com', TRUE), + array('http://google.com/', TRUE), + array('http://google.com/?q=abc', TRUE), + array('http://google.com/#hash', TRUE), + array('http://localhost', TRUE), + array('http://hello-world.pl', TRUE), + array('http://hello--world.pl', TRUE), + array('http://h.e.l.l.0.pl', TRUE), + array('http://server.tld/get/info', TRUE), + array('http://127.0.0.1', TRUE), + array('http://127.0.0.1:80', TRUE), + array('http://user@127.0.0.1', TRUE), + array('http://user:pass@127.0.0.1', TRUE), + array('ftp://my.server.com', TRUE), + array('rss+xml://rss.example.com', TRUE), + + array('http://google.2com', FALSE), + array('http://google.com?q=abc', FALSE), + array('http://google.com#hash', FALSE), + array('http://hello-.pl', FALSE), + array('http://hel.-lo.world.pl', FALSE), + array('http://ww£.google.com', FALSE), + array('http://127.0.0.1234', FALSE), + array('http://127.0.0.1.1', FALSE), + array('http://user:@127.0.0.1', FALSE), + array("http://finalnewline.com\n", FALSE), + // Empty test + array('', FALSE), + array(NULL, FALSE), + array(FALSE, FALSE), + ); + + $data[] = array('http://'.str_repeat('123456789.', 25).'com/', TRUE); // 253 chars + $data[] = array('http://'.str_repeat('123456789.', 25).'info/', FALSE); // 254 chars + + return $data; + } + + /** + * Tests Valid::url() + * + * @test + * @dataProvider provider_url + * @param string $url The url to test + * @param boolean $expected Is it valid? + */ + public function test_url($url, $expected) + { + $this->assertSame( + $expected, + Valid::url($url) + ); + } + + /** + * DataProvider for the valid::matches() test + */ + public function provider_matches() + { + return array( + array(array('a' => 'hello', 'b' => 'hello'), 'a', 'b', TRUE), + array(array('a' => 'hello', 'b' => 'hello '), 'a', 'b', FALSE), + array(array('a' => '1', 'b' => 1), 'a', 'b', FALSE), + // Empty test + array(array('a' => '', 'b' => 'hello'), 'a', 'b', FALSE), + array(array('a' => NULL, 'b' => 'hello'), 'a', 'b', FALSE), + array(array('a' => FALSE, 'b' => 'hello'), 'a', 'b', FALSE), + ); + } + + /** + * Tests Valid::matches() + * + * Tests if a field matches another from an array of data + * + * @test + * @dataProvider provider_matches + * @param array $data Array of fields + * @param integer $field First field name + * @param integer $match Field name that must match $field in $data + * @param boolean $expected Do the two fields match? + */ + public function test_matches($data, $field, $match, $expected) + { + $this->AssertSame( + $expected, + Valid::matches($data, $field, $match) + ); + } +} diff --git a/system/tests/kohana/ValidationTest.php b/system/tests/kohana/ValidationTest.php new file mode 100644 index 0000000..c9fce26 --- /dev/null +++ b/system/tests/kohana/ValidationTest.php @@ -0,0 +1,676 @@ + + * @copyright (c) 2008-2012 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_ValidationTest extends Unittest_TestCase +{ + /** + * Tests Validation::factory() + * + * Makes sure that the factory method returns an instance of Validation lib + * and that it uses the variables passed + * + * @test + */ + public function test_factory_method_returns_instance_with_values() + { + $values = array( + 'this' => 'something else', + 'writing tests' => 'sucks', + 'why the hell' => 'amIDoingThis', + ); + + $instance = Validation::factory($values); + + $this->assertTrue($instance instanceof Validation); + + $this->assertSame( + $values, + $instance->data() + ); + } + + /** + * When we copy() a validation object, we should have a new validation object + * with the exact same attributes, apart from the data, which should be the + * same as the array we pass to copy() + * + * @test + * @covers Validation::copy + */ + public function test_copy_copies_all_attributes_except_data() + { + $validation = new Validation(array('foo' => 'bar', 'fud' => 'fear, uncertainty, doubt', 'num' => 9)); + + $validation->rule('num', 'is_int')->rule('foo', 'is_string'); + + $copy_data = array('foo' => 'no', 'fud' => 'maybe', 'num' => 42); + + $copy = $validation->copy($copy_data); + + $this->assertNotSame($validation, $copy); + + foreach (array('_rules', '_bound', '_labels', '_empty_rules', '_errors') as $attribute) + { + // This is just an easy way to check that the attributes are identical + // Without hardcoding the expected values + $this->assertAttributeSame( + self::readAttribute($validation, $attribute), + $attribute, + $copy + ); + } + + $this->assertSame($copy_data, $copy->data()); + } + + /** + * When the validation object is initially created there should be no labels + * specified + * + * @test + */ + public function test_initially_there_are_no_labels() + { + $validation = new Validation(array()); + + $this->assertAttributeSame(array(), '_labels', $validation); + } + + /** + * Adding a label to a field should set it in the labels array + * If the label already exists it should overwrite it + * + * In both cases thefunction should return a reference to $this + * + * @test + * @covers Validation::label + */ + public function test_label_adds_and_overwrites_label_and_returns_this() + { + $validation = new Validation(array()); + + $this->assertSame($validation, $validation->label('email', 'Email Address')); + + $this->assertAttributeSame(array('email' => 'Email Address'), '_labels', $validation); + + $this->assertSame($validation, $validation->label('email', 'Your Email')); + + $validation->label('name', 'Your Name'); + + $this->assertAttributeSame( + array('email' => 'Your Email', 'name' => 'Your Name'), + '_labels', + $validation + ); + } + + /** + * Using labels() we should be able to add / overwrite multiple labels + * + * The function should also return $this for chaining purposes + * + * @test + * @covers Validation::labels + */ + public function test_labels_adds_and_overwrites_multiple_labels_and_returns_this() + { + $validation = new Validation(array()); + $initial_data = array('kung fu' => 'fighting', 'fast' => 'cheetah'); + + $this->assertSame($validation, $validation->labels($initial_data)); + + $this->assertAttributeSame($initial_data, '_labels', $validation); + + $this->assertSame($validation, $validation->labels(array('fast' => 'lightning'))); + + $this->assertAttributeSame( + array('fast' => 'lightning', 'kung fu' => 'fighting'), + '_labels', + $validation + ); + } + + /** + * Using bind() we should be able to add / overwrite multiple bound variables + * + * The function should also return $this for chaining purposes + * + * @test + * @covers Validation::bind + */ + public function test_bind_adds_and_overwrites_multiple_variables_and_returns_this() + { + $validation = new Validation(array()); + $data = array('kung fu' => 'fighting', 'fast' => 'cheetah'); + $bound = array(':foo' => 'some value'); + + // Test binding an array of values + $this->assertSame($validation, $validation->bind($bound)); + $this->assertAttributeSame($bound, '_bound', $validation); + + // Test binding one value + $this->assertSame($validation, $validation->bind(':foo', 'some other value')); + $this->assertAttributeSame(array(':foo' => 'some other value'), '_bound', $validation); + } + + /** + * We should be able to used bound variables in callbacks + * + * @test + * @covers Validation::check + */ + public function test_bound_callback() + { + $data = array( + 'kung fu' => 'fighting', + 'fast' => 'cheetah', + ); + $validation = new Validation($data); + $validation->bind(':class', 'Valid') + // Use the bound value in a callback + ->rule('fast', array(':class', 'max_length'), array(':value', 2)); + + // The rule should have run and check() should fail + $this->assertSame($validation->check(), FALSE); + } + + /** + * Provides test data for test_check + * + * @return array + */ + public function provider_check() + { + // $data_array, $rules, $labels, $first_expected, $expected_error + return array( + array( + array('foo' => 'bar'), + array('foo' => array(array('not_empty', NULL))), + array(), + TRUE, + array(), + ), + array( + array('unit' => 'test'), + array( + 'foo' => array(array('not_empty', NULL)), + 'unit' => array(array('min_length', array(':value', 6)) + ), + ), + array(), + FALSE, + array( + 'foo' => 'foo must not be empty', + 'unit' => 'unit must be at least 6 characters long' + ), + ), + array( + array('foo' => 'bar'), + array( + // Tests wildcard rules + TRUE => array(array('min_length', array(':value', 4))), + 'foo' => array( + array('not_empty', NULL), + // Tests the array syntax for callbacks + array(array('Valid', 'exact_length'), array(':value', 3)), + // Tests the Class::method syntax for callbacks + array('Valid::exact_length', array(':value', 3)), + // Tests the lambda function syntax for callbacks + // Commented out for PHP 5.2 support + // array(function($value){return TRUE;}, array(':value')), + // Tests using a function as a rule + array('is_string', array(':value')), + ), + // Tests that rules do not run on empty fields unless they are in _empty_rules + 'unit' => array(array('exact_length', array(':value', 4))), + ), + array(), + FALSE, + array('foo' => 'foo must be at least 4 characters long'), + ), + // Switch things around and make :value an array + array( + array('foo' => array('test', 'data')), + array('foo' => array(array('in_array', array('kohana', ':value')))), + array(), + FALSE, + array('foo' => 'foo must be one of the available options'), + ), + // Test wildcard rules with no other rules + array( + array('foo' => array('test')), + array(TRUE => array(array('is_string', array(':value')))), + array('foo' => 'foo'), + FALSE, + array('foo' => '1.foo.is_string'), + ), + // Test array rules use method as error name + array( + array('foo' => 'test'), + array('foo' => array(array(array('Valid', 'min_length'), array(':value', 10)))), + array(), + FALSE, + array('foo' => 'foo must be at least 10 characters long'), + ), + ); + } + + /** + * Tests Validation::check() + * + * @test + * @covers Validation::check + * @covers Validation::rule + * @covers Validation::rules + * @covers Validation::errors + * @covers Validation::error + * @dataProvider provider_check + * @param array $array The array of data + * @param array $rules The array of rules + * @param array $labels The array of labels + * @param boolean $expected Is it valid? + * @param boolean $expected_errors Array of expected errors + */ + public function test_check($array, $rules, $labels, $expected, $expected_errors) + { + $validation = new Validation($array); + + foreach ($labels as $field => $label) + { + $validation->label($field, $label); + } + + foreach ($rules as $field => $field_rules) + { + foreach ($field_rules as $rule) + $validation->rule($field, $rule[0], $rule[1]); + } + + $status = $validation->check(); + $errors = $validation->errors(TRUE); + $this->assertSame($expected, $status); + $this->assertSame($expected_errors, $errors); + + $validation = new validation($array); + foreach ($rules as $field => $rules) + { + $validation->rules($field, $rules); + } + $validation->labels($labels); + + $this->assertSame($expected, $validation->check()); + } + + /** + * Tests Validation::check() + * + * @test + * @covers Validation::check + */ + public function test_check_stops_when_error_added_by_callback() + { + $validation = new Validation(array( + 'foo' => 'foo', + )); + + $validation + ->rule('foo', array($this, '_validation_callback'), array(':validation')) + // This rule should never run + ->rule('foo', 'min_length', array(':value', 20)); + + $validation->check(); + $errors = $validation->errors(); + + $expected = array( + 'foo' => array( + 0 => '_validation_callback', + 1 => NULL, + ), + ); + + $this->assertSame($errors, $expected); + } + + public function _validation_callback(Validation $object) + { + // Simply add the error + $object->error('foo', '_validation_callback'); + } + + /** + * Provides test data for test_errors() + * + * @return array + */ + public function provider_errors() + { + // [data, rules, expected], ... + return array( + // No Error + array( + array('username' => 'frank'), + array('username' => array(array('not_empty', NULL))), + array(), + ), + // Error from message file + array( + array('username' => ''), + array('username' => array(array('not_empty', NULL))), + array('username' => 'username must not be empty'), + ), + // No error message exists, display the path expected + array( + array('username' => 'John'), + array('username' => array(array('strpos', array(':value', 'Kohana')))), + array('username' => 'Validation.username.strpos'), + ), + ); + } + + /** + * Tests Validation::errors() + * + * @test + * @covers Validation::errors + * @dataProvider provider_errors + * @param array $array The array of data + * @param array $rules The array of rules + * @param array $expected Array of expected errors + */ + public function test_errors($array, $rules, $expected) + { + $validation = Validation::factory($array); + + foreach ($rules as $field => $field_rules) + { + $validation->rules($field, $field_rules); + } + + $validation->check(); + + $this->assertSame($expected, $validation->errors('Validation', FALSE)); + // Should be able to get raw errors array + $this->assertAttributeSame($validation->errors(NULL), '_errors', $validation); + } + + /** + * Provides test data for test_translated_errors() + * + * @return array + */ + public function provider_translated_errors() + { + // [data, rules, expected], ... + return array( + array( + array('Spanish' => ''), + array('Spanish' => array(array('not_empty', NULL))), + // Errors are not translated yet so only the label will translate + array('Spanish' => 'Español must not be empty'), + array('Spanish' => 'Spanish must not be empty'), + ), + ); + } + + /** + * Tests Validation::errors() + * + * @test + * @covers Validation::errors + * @dataProvider provider_translated_errors + * @param array $data The array of data to test + * @param array $rules The array of rules to add + * @param array $translated_expected The array of expected errors when translated + * @param array $untranslated_expected The array of expected errors when not translated + */ + public function test_translated_errors($data, $rules, $translated_expected, $untranslated_expected) + { + $validation = Validation::factory($data); + + $current = i18n::lang(); + i18n::lang('es'); + + foreach ($rules as $field => $field_rules) + { + $validation->rules($field, $field_rules); + } + + $validation->check(); + + $result_1 = $validation->errors('Validation', TRUE); + $result_2 = $validation->errors('Validation', 'en'); + $result_3 = $validation->errors('Validation', FALSE); + + // Restore the current language + i18n::lang($current); + + $this->assertSame($translated_expected, $result_1); + $this->assertSame($translated_expected, $result_2); + $this->assertSame($untranslated_expected, $result_3); + } + + /** + * Tests Validation::errors() + * + * @test + * @covers Validation::errors + */ + public function test_parameter_labels() + { + $validation = Validation::factory(array('foo' => 'bar')) + ->rule('foo', 'equals', array(':value', 'something')) + ->label('something', 'Spanish'); + + $current = i18n::lang(); + i18n::lang('es'); + + $validation->check(); + + $translated_expected = array('foo' => 'foo must equal Español'); + $untranslated_expected = array('foo' => 'foo must equal Spanish'); + + $result_1 = $validation->errors('Validation', TRUE); + $result_2 = $validation->errors('Validation', 'en'); + $result_3 = $validation->errors('Validation', FALSE); + + // Restore the current language + i18n::lang($current); + + $this->assertSame($translated_expected, $result_1); + $this->assertSame($translated_expected, $result_2); + $this->assertSame($untranslated_expected, $result_3); + } + + /** + * Tests Validation::errors() + * + * @test + * @covers Validation::errors + */ + public function test_arrays_in_parameters() + { + $validation = Validation::factory(array('foo' => 'bar')) + ->rule('foo', 'equals', array(':value', array('one', 'two'))); + + $validation->check(); + + $expected = array('foo' => 'foo must equal one, two'); + + $this->assertSame($expected, $validation->errors('Validation', FALSE)); + } + + /** + * Tests Validation::check() + * + * @test + * @covers Validation::check + */ + public function test_data_stays_unaltered() + { + $validation = Validation::factory(array('foo' => 'bar')) + ->rule('something', 'not_empty'); + + $before = $validation->data(); + $validation->check(); + $after = $validation->data(); + + $expected = array('foo' => 'bar'); + + $this->assertSame($expected, $before); + $this->assertSame($expected, $after); + } + + /** + * Tests Validation::errors() + * + * @test + * @covers Validation::errors + */ + public function test_object_parameters_not_in_messages() + { + $validation = Validation::factory(array('foo' => 'foo')) + ->rule('bar', 'matches', array(':validation', ':field', 'foo')); + + $validation->check(); + $errors = $validation->errors('validation'); + $expected = array('bar' => 'bar must be the same as foo'); + + $this->assertSame($expected, $errors); + } + + /** + * Tests Validation::as_array() + * + * @test + * @covers Validation::as_array + */ + public function test_as_array_returns_original_array() + { + $data = array( + 'one' => 'hello', + 'two' => 'world', + 'ten' => '', + ); + + $validation = Validation::factory($data); + + $this->assertSame($data, $validation->as_array()); + } + + /** + * Tests Validation::data() + * + * @test + * @covers Validation::data + */ + public function test_data_returns_original_array() + { + $data = array( + 'one' => 'hello', + 'two' => 'world', + 'ten' => '', + ); + + $validation = Validation::factory($data); + + $this->assertSame($data, $validation->data()); + } + + // @codingStandardsIgnoreStart + public function test_offsetExists() + // @codingStandardsIgnoreEnd + { + $array = array( + 'one' => 'Hello', + 'two' => 'World', + 'ten' => NULL, + ); + + $validation = Validation::factory($array); + + $this->assertTrue(isset($validation['one'])); + $this->assertFalse(isset($validation['ten'])); + $this->assertFalse(isset($validation['five'])); + } + + // @codingStandardsIgnoreStart + public function test_offsetSet_throws_exception() + // @codingStandardsIgnoreEnd + { + $this->setExpectedException('Kohana_Exception'); + + $validation = Validation::factory(array()); + + // Validation is read-only + $validation['field'] = 'something'; + } + + // @codingStandardsIgnoreStart + public function test_offsetGet() + // @codingStandardsIgnoreEnd + { + $array = array( + 'one' => 'Hello', + 'two' => 'World', + 'ten' => NULL, + ); + + $validation = Validation::factory($array); + + $this->assertSame($array['one'], $validation['one']); + $this->assertSame($array['two'], $validation['two']); + $this->assertSame($array['ten'], $validation['ten']); + } + + // @codingStandardsIgnoreStart + public function test_offsetUnset() + // @codingStandardsIgnoreEnd + { + $this->setExpectedException('Kohana_Exception'); + + $validation = Validation::factory(array( + 'one' => 'Hello, World!', + )); + + // Validation is read-only + unset($validation['one']); + } + + /** + * http://dev.kohanaframework.org/issues/4365 + * + * @test + * @covers Validation::errors + */ + public function test_error_type_check() + { + $array = array( + 'email' => 'not an email address', + ); + + $validation = Validation::factory($array) + ->rule('email', 'not_empty') + ->rule('email', 'email') + ; + + $validation->check(); + + $errors = $validation->errors('tests/validation/error_type_check'); + + $this->assertSame($errors, $validation->errors('validation')); + } + +} diff --git a/system/tests/kohana/ViewTest.php b/system/tests/kohana/ViewTest.php new file mode 100644 index 0000000..543f411 --- /dev/null +++ b/system/tests/kohana/ViewTest.php @@ -0,0 +1,83 @@ + realpath(dirname(__FILE__).'/../test_data/') + ); + Kohana::modules($new_modules); + } + + /** + * Restores the module list + * + * @return null + */ + // @codingStandardsIgnoreStart + public static function teardownAfterClass() + // @codingStandardsIgnoreEnd + { + Kohana::modules(self::$old_modules); + } + + /** + * Provider for test_instaniate + * + * @return array + */ + public function provider_instantiate() + { + return array( + array('kohana/error', FALSE), + array('test.css', FALSE), + array('doesnt_exist', TRUE), + ); + } + + /** + * Tests that we can instantiate a view file + * + * @test + * @dataProvider provider_instantiate + * + * @return null + */ + public function test_instantiate($path, $expects_exception) + { + try + { + $view = new View($path); + $this->assertSame(FALSE, $expects_exception); + } + catch(View_Exception $e) + { + $this->assertSame(TRUE, $expects_exception); + } + } +} diff --git a/system/tests/kohana/request/ClientTest.php b/system/tests/kohana/request/ClientTest.php new file mode 100644 index 0000000..fffc1d5 --- /dev/null +++ b/system/tests/kohana/request/ClientTest.php @@ -0,0 +1,488 @@ +getProperty('_routes'); + $routes_prop->setAccessible(TRUE); + + self::$_original_routes = $routes_prop->getValue('Route'); + + $routes = array( + 'ko_request_clienttest' => new Route('//',array('data'=>'.+')) + ) + self::$_original_routes; + + $routes_prop->setValue('Route',$routes); + + } + + // @codingStandardsIgnoreStart - PHPUnit does not follow standards + /** + * Resets the application's routes to their state prior to this test case + */ + public static function tearDownAfterClass() + { + // @codingStandardsIgnoreEnd + // Reset routes + $route_class = new ReflectionClass('Route'); + $routes_prop = $route_class->getProperty('_routes'); + $routes_prop->setAccessible(TRUE); + $routes_prop->setValue('Route',self::$_original_routes); + + parent::tearDownAfterClass(); + } + + // @codingStandardsIgnoreStart - PHPUnit does not follow standards + public function setUp() + { + // @codingStandardsIgnoreEnd + parent::setUp(); + $this->_initial_request = Request::$initial; + Request::$initial = new Request('/'); + } + + // @codingStandardsIgnoreStart - PHPUnit does not follow standards + public function tearDown() + { + // @codingStandardsIgnoreEnd + Request::$initial = $this->_initial_request; + parent::tearDown(); + } + + /** + * Generates an internal URI to the [Controller_RequestClientDummy] shunt + * controller - the URI contains an encoded form of the required server + * response. + * + * @param string $status HTTP response code to issue + * @param array $headers HTTP headers to send with the response + * @param string $body A string to send back as response body (included in the JSON response) + * @return string + */ + protected function _dummy_uri($status, $headers, $body) + { + $data = array( + 'status' => $status, + 'header' => $headers, + 'body' => $body + ); + return "/requestclientdummy/fake".'/'.urlencode(http_build_query($data)); + } + + /** + * Shortcut method to generate a simple redirect URI - the first request will + * receive a redirect with the given HTTP status code and the second will + * receive a 200 response. The 'body' data value in the first response will + * be 'not-followed' and in the second response it will be 'followed'. This + * allows easy assertion that a redirect has taken place. + * + * @param string $status HTTP response code to issue + * @return string + */ + protected function _dummy_redirect_uri($status) + { + return $this->_dummy_uri($status, + array('Location' => $this->_dummy_uri(200, NULL, 'followed')), + 'not-followed'); + } + + /** + * Provider for test_follows_redirects + * @return array + */ + public function provider_follows_redirects() + { + return array( + array(TRUE, $this->_dummy_uri(200, NULL, 'not-followed'), 'not-followed'), + array(TRUE, $this->_dummy_redirect_uri(200), 'not-followed'), + array(TRUE, $this->_dummy_redirect_uri(302), 'followed'), + array(FALSE, $this->_dummy_redirect_uri(302), 'not-followed'), + ); + } + + /** + * Tests that the client optionally follows properly formed redirects + * + * @dataProvider provider_follows_redirects + * + * @param bool $follow Option value to set + * @param string $request_url URL to request initially (contains data to set up redirect etc) + * @param string $expect_body Body text expected in the eventual result + */ + public function test_follows_redirects($follow, $request_url, $expect_body) + { + $response = Request::factory($request_url, + array('follow' => $follow)) + ->execute(); + + $data = json_decode($response->body(), TRUE); + $this->assertEquals($expect_body, $data['body']); + } + + /** + * Tests that only specified headers are resent following a redirect + */ + public function test_follows_with_headers() + { + $response = Request::factory( + $this->_dummy_redirect_uri(301), + array( + 'follow' => TRUE, + 'follow_headers' => array('Authorization', 'X-Follow-With-Value') + )) + ->headers(array( + 'Authorization' => 'follow', + 'X-Follow-With-Value' => 'follow', + 'X-Not-In-Follow' => 'no-follow' + )) + ->execute(); + + $data = json_decode($response->body(),TRUE); + $headers = $data['rq_headers']; + + $this->assertEquals('followed', $data['body']); + $this->assertEquals('follow', $headers['authorization']); + $this->assertEquals('follow', $headers['x-follow-with-value']); + $this->assertFalse(isset($headers['x-not-in-follow']), 'X-Not-In-Follow should not be passed to next request'); + } + + /** + * Provider for test_follows_with_strict_method + * + * @return array + */ + public function provider_follows_with_strict_method() + { + return array( + array(201, NULL, Request::POST, Request::GET), + array(301, NULL, Request::GET, Request::GET), + array(302, TRUE, Request::POST, Request::POST), + array(302, FALSE, Request::POST, Request::GET), + array(303, NULL, Request::POST, Request::GET), + array(307, NULL, Request::POST, Request::POST), + ); + } + + /** + * Tests that the correct method is used (allowing for the strict_redirect setting) + * for follow requests. + * + * @dataProvider provider_follows_with_strict_method + * + * @param string $status_code HTTP response code to fake + * @param bool $strict_redirect Option value to set + * @param string $orig_method Request method for the original request + * @param string $expect_method Request method expected for the follow request + */ + public function test_follows_with_strict_method($status_code, $strict_redirect, $orig_method, $expect_method) + { + $response = Request::factory($this->_dummy_redirect_uri($status_code), + array( + 'follow' => TRUE, + 'strict_redirect' => $strict_redirect + )) + ->method($orig_method) + ->execute(); + + $data = json_decode($response->body(), TRUE); + + $this->assertEquals('followed', $data['body']); + $this->assertEquals($expect_method, $data['rq_method']); + } + + /** + * Provider for test_follows_with_body_if_not_get + * + * @return array + */ + public function provider_follows_with_body_if_not_get() + { + return array( + array('GET','301',NULL), + array('POST','303',NULL), + array('POST','307','foo-bar') + ); + } + + /** + * Tests that the original request body is sent when following a redirect + * (unless redirect method is GET) + * + * @dataProvider provider_follows_with_body_if_not_get + * @depends test_follows_with_strict_method + * @depends test_follows_redirects + * + * @param string $original_method Request method to use for the original request + * @param string $status Redirect status that will be issued + * @param string $expect_body Expected value of body() in the second request + */ + public function test_follows_with_body_if_not_get($original_method, $status, $expect_body) + { + $response = Request::factory($this->_dummy_redirect_uri($status), + array('follow' => TRUE)) + ->method($original_method) + ->body('foo-bar') + ->execute(); + + $data = json_decode($response->body(), TRUE); + + $this->assertEquals('followed', $data['body']); + $this->assertEquals($expect_body, $data['rq_body']); + } + + /** + * Provider for test_triggers_header_callbacks + * + * @return array + */ + public function provider_triggers_header_callbacks() + { + return array( + // Straightforward response manipulation + array( + array('X-test-1' => + function($request, $response, $client) + { + $response->body(json_encode(array('body'=>'test1-body-changed'))); + return $response; + }), + $this->_dummy_uri(200, array('X-test-1' => 'foo'), 'test1-body'), + 'test1-body-changed' + ), + // Subsequent request execution + array( + array('X-test-2' => + function($request, $response, $client) + { + return Request::factory($response->headers('X-test-2')); + }), + $this->_dummy_uri(200, + array('X-test-2' => $this->_dummy_uri(200, NULL, 'test2-subsequent-body')), + 'test2-orig-body'), + 'test2-subsequent-body' + ), + // No callbacks triggered + array( + array('X-test-3' => + function ($request, $response, $client) + { + throw new Exception("Unexpected execution of X-test-3 callback"); + }), + $this->_dummy_uri(200, array('X-test-1' => 'foo'), 'test3-body'), + 'test3-body' + ), + // Callbacks not triggered once a previous callback has created a new response + array( + array( + 'X-test-1' => + function($request, $response, $client) + { + return Request::factory($response->headers('X-test-1')); + }, + 'X-test-2' => + function($request, $response, $client) + { + return Request::factory($response->headers('X-test-2')); + } + ), + $this->_dummy_uri(200, + array( + 'X-test-1' => $this->_dummy_uri(200, NULL, 'test1-subsequent-body'), + 'X-test-2' => $this->_dummy_uri(200, NULL, 'test2-subsequent-body') + ), + 'test2-orig-body'), + 'test1-subsequent-body' + ), + // Nested callbacks are supported if callback creates new request + array( + array( + 'X-test-1' => + function($request, $response, $client) + { + return Request::factory($response->headers('X-test-1')); + }, + 'X-test-2' => + function($request, $response, $client) + { + return Request::factory($response->headers('X-test-2')); + } + ), + $this->_dummy_uri(200, + array( + 'X-test-1' => $this->_dummy_uri( + 200, + array('X-test-2' => $this->_dummy_uri(200, NULL, 'test2-subsequent-body')), + 'test1-subsequent-body'), + ), + 'test-orig-body'), + 'test2-subsequent-body' + ), + ); + } + + /** + * Tests that header callbacks are triggered in sequence when specific headers + * are present in the response + * + * @dataProvider provider_triggers_header_callbacks + * + * @param array $callbacks Array of header callbacks + * @param array $headers Headers that will be received in the response + * @param string $expect_body Response body content to expect + */ + public function test_triggers_header_callbacks($callbacks, $uri, $expect_body) + { + $response = Request::factory($uri, + array('header_callbacks' => $callbacks)) + ->execute(); + + $data = json_decode($response->body(), TRUE); + + $this->assertEquals($expect_body, $data['body']); + } + + /** + * Tests that the Request_Client is protected from too many recursions of + * requests triggered by header callbacks. + * + */ + public function test_deep_recursive_callbacks_are_aborted() + { + $uri = $this->_dummy_uri('200', array('x-cb' => '1'), 'body'); + + // Temporary property to track requests + $this->requests_executed = 0; + + try + { + $response = Request::factory( + $uri, + array( + 'header_callbacks' => array( + 'x-cb' => + function ($request, $response, $client) + { + $client->callback_params('testcase')->requests_executed++; + // Recurse into a new request + return Request::factory($request->uri()); + }), + 'max_callback_depth' => 2, + 'callback_params' => array( + 'testcase' => $this, + ) + )) + ->execute(); + } + catch (Request_Client_Recursion_Exception $e) + { + // Verify that two requests were executed + $this->assertEquals(2, $this->requests_executed); + return; + } + + $this->fail('Expected Request_Client_Recursion_Exception was not thrown'); + } + + /** + * Header callback for testing that arbitrary callback_params are available + * to the callback. + * + * @param Request $request + * @param Response $response + * @param Request_Client $client + */ + public function callback_assert_params($request, $response, $client) + { + $this->assertEquals('foo', $client->callback_params('constructor_param')); + $this->assertEquals('bar', $client->callback_params('setter_param')); + $response->body('assertions_ran'); + } + + /** + * Test that arbitrary callback_params can be passed to the callback through + * the Request_Client and are assigned to subsequent requests + */ + public function test_client_can_hold_params_for_callbacks() + { + // Test with param in constructor + $request = Request::factory( + $this->_dummy_uri( + 302, + array('Location' => $this->_dummy_uri('200',array('X-cb'=>'1'), 'followed')), + 'not-followed'), + array( + 'follow' => TRUE, + 'header_callbacks' => array( + 'x-cb' => array($this, 'callback_assert_params'), + 'location' => 'Request_Client::on_header_location', + ), + 'callback_params' => array( + 'constructor_param' => 'foo' + ) + )); + + // Test passing param to setter + $request->client()->callback_params('setter_param', 'bar'); + + // Callback will throw assertion exceptions when executed + $response = $request->execute(); + $this->assertEquals('assertions_ran', $response->body()); + } + +} // End Kohana_Request_ClientTest + + +/** + * Dummy controller class that acts as a shunt - passing back request information + * in the response to allow inspection. + */ +class Controller_RequestClientDummy extends Controller { + + /** + * Takes a urlencoded 'data' parameter from the route and uses it to craft a + * response. Redirect chains can be tested by passing another encoded uri + * as a location header with an appropriate status code. + */ + public function action_fake() + { + parse_str(urldecode($this->request->param('data')), $data); + $this->response->status(Arr::get($data, 'status', 200)); + $this->response->headers(Arr::get($data, 'header', array())); + $this->response->body(json_encode(array( + 'body'=> Arr::get($data,'body','ok'), + 'rq_headers' => $this->request->headers(), + 'rq_body' => $this->request->body(), + 'rq_method' => $this->request->method(), + ))); + } + +} // End Controller_RequestClientDummy diff --git a/system/tests/kohana/request/client/ExternalTest.php b/system/tests/kohana/request/client/ExternalTest.php new file mode 100644 index 0000000..63d792f --- /dev/null +++ b/system/tests/kohana/request/client/ExternalTest.php @@ -0,0 +1,191 @@ +assertInstanceOf($expected, Request_Client_External::factory($params, $client)); + } + + /** + * Data provider for test_options + * + * @return array + */ + public function provider_options() + { + return array( + array( + NULL, + NULL, + array() + ), + array( + array('foo' => 'bar', 'stfu' => 'snafu'), + NULL, + array('foo' => 'bar', 'stfu' => 'snafu') + ), + array( + 'foo', + 'bar', + array('foo' => 'bar') + ), + array( + array('foo' => 'bar'), + 'foo', + array('foo' => 'bar') + ) + ); + } + + /** + * Tests the [Request_Client_External::options()] method + * + * @dataProvider provider_options + * + * @param mixed $key key + * @param mixed $value value + * @param array $expected expected + * @return void + */ + public function test_options($key, $value, $expected) + { + // Create a mock external client + $client = new Request_Client_Stream; + + $client->options($key, $value); + $this->assertSame($expected, $client->options()); + } + + /** + * Data provider for test_execute + * + * @return array + */ + public function provider_execute() + { + $json = '{"foo": "bar", "snafu": "stfu"}'; + $post = array('foo' => 'bar', 'snafu' => 'stfu'); + + return array( + array( + 'application/json', + $json, + array(), + array( + 'content-type' => 'application/json', + 'body' => $json + ) + ), + array( + 'application/json', + $json, + $post, + array( + 'content-type' => 'application/x-www-form-urlencoded', + 'body' => http_build_query($post, NULL, '&') + ) + ) + ); + } + + /** + * Tests the [Request_Client_External::_send_message()] method + * + * @dataProvider provider_execute + * + * @return void + */ + public function test_execute($content_type, $body, $post, $expected) + { + $old_request = Request::$initial; + Request::$initial = TRUE; + + // Create a mock Request + $request = new Request('http://kohanaframework.org/'); + $request->method(HTTP_Request::POST) + ->headers('content-type', $content_type) + ->body($body) + ->post($post); + + $client = $this->getMock('Request_Client_External', array('_send_message')); + $client->expects($this->once()) + ->method('_send_message') + ->with($request) + ->will($this->returnValue($this->getMock('Response'))); + + $request->client($client); + + $this->assertInstanceOf('Response', $request->execute()); + $this->assertSame($expected['body'], $request->body()); + $this->assertSame($expected['content-type'], (string) $request->headers('content-type')); + + Request::$initial = $old_request; + } +} \ No newline at end of file diff --git a/system/tests/kohana/request/client/InternalTest.php b/system/tests/kohana/request/client/InternalTest.php new file mode 100644 index 0000000..2c87893 --- /dev/null +++ b/system/tests/kohana/request/client/InternalTest.php @@ -0,0 +1,68 @@ +getMock('Request', array('directory', 'controller', 'action', 'uri', 'response'), array($uri)); + + $request->expects($this->any()) + ->method('directory') + ->will($this->returnValue($directory)); + + $request->expects($this->any()) + ->method('controller') + ->will($this->returnValue($controller)); + + $request->expects($this->any()) + ->method('action') + ->will($this->returnValue($action)); + + $request->expects($this->any()) + ->method('uri') + ->will($this->returnValue($uri)); + + $request->expects($this->any()) + ->method('response') + ->will($this->returnValue($this->getMock('Response'))); + + $internal_client = new Request_Client_Internal; + + $response = $internal_client->execute($request); + + $this->assertSame($expected, $response->status()); + } +} \ No newline at end of file diff --git a/system/tests/test_data/callback_routes.php b/system/tests/test_data/callback_routes.php new file mode 100644 index 0000000..6e69f5a --- /dev/null +++ b/system/tests/test_data/callback_routes.php @@ -0,0 +1,100 @@ + 'welcome', + 'action' => 'index', + ); + } + + /** + * Route callback for test_required_parameters_are_needed + * + * @return array + */ + public static function required_parameters_are_needed($uri) + { + if (substr($uri, 0, 5) == 'admin') + { + return array( + 'controller' => 'foo', + 'action' => 'bar', + ); + } + } + + /** + * Route callback for test reverse_routing_returns_routes_uri_if_route_is_static + * + * @return array + */ + public static function reverse_routing_returns_routes_uri_if_route_is_static($uri) + { + if ($uri == 'info/about_us') + { + return array( + + ); + } + } + + /** + * Route callback for test route_filter_modify_params + * + * @return array + */ + public static function route_filter_modify_params_array(Route $route, $params) + { + $params['action'] = 'modified'; + + return $params; + } + + /** + * Route callback for test route_filter_modify_params + * + * @return array + */ + public static function route_filter_modify_params_false(Route $route, $params) + { + return FALSE; + } + +} \ No newline at end of file diff --git a/system/tests/test_data/github.png b/system/tests/test_data/github.png new file mode 100644 index 0000000000000000000000000000000000000000..81176849612fc7d246de8e4e03a68d51e1a90ca8 GIT binary patch literal 5101 zcmVd4%jY}e!hzlAQ)I^Qqb4d(}qM#s~ z%Az0|6a+yOg#UkE{Z!*H%>N?-mwWGfeNLT!X1b=Qx~seDtLg?Lspg9#Xf>!4=niNJ z=xPnLy8_e_RKoYBpcA0oQr(t*pgExDAT7uUqyhPW@?N$PtyWtr!nFkzv0!Bs9v=Q1 zKKN!e+kT*IcgT<-H_`4k(BwK)$oopUh~nepgCW-zP{Ws1NNp%(yP9w#MvTyMdA%BK zpI5d!Z{9r7?)ExV$Z)kjXJusx+3c#K5aSva?k%;4(b3W3(ytnA7bqVX85xPo+&Waq zBjtno_3KMbn>H1<&#KZ`O)KP~+CvhMi*q&FHZnB9>QEs@2K+$*CQWu-DrBqL!|Btf z>z(EQ=EbP0FVw4H0XBnrfM$sY9v&XjqD70u?T`Fx25Q8JNgxxFC0mA~{!bR;T4>XY zl;ZF2e=;H>LfW%uk9htQmhVcwc4O3(%{G{RGY1S9FbK=NImkr%|5EdXpf1QA)LT(C zNDZ0Xx^+t+lO8I~ol^39dwUE1^>x_I{z|zlEiHvy1yvX;9o4H%NwFfX>In*;s_~;E z;_~sowBs-`dgXWP88BsCInbjBG>J*ASb=OoxA@*%;Ty>O6e^sYm0ClOA3v7t?ChkB zjEo{~r_hk_W@cvc+h(8+pnR64sFHpg%lwt_VfrAd1*lloCu2kH8yg!d(Vr8jF(?gm zuPO?94YY~vdbwq2VPTQgrcE2F>j-)Svos^Rc-`3u|c~k4@=~=|9U2e`&?CtFrzf7d= zF3ZPMiCkS>lhIf)0>EQx^>J}=I)mD}b?aZN((a{8mo#jPd#fpg6j)gsTefTo!~2~S z{a>rbo12?U-=c5Y%Xk~9@dgD2>0}EC2=G(ZVM0_YSO5O~pA-}ncK=tIlqA@Gu)pHsJ1wxQwLwp!O%c`_n zSXkylsui0|oH&tw4nw}4`V?$#ZmuKt4V8LjjYcE<-YchVLPJ9(thBdrc~q{DC2A{U z#*7)#`Sa(csHiAu&YU^=y<@$4QkO1WghJ};gLJ%-!tk?h@ZiDes%g{r-Wmi6dW8(mwWZS4?ictPf83V3afyq3@Wcdg9bXF zqObV+`ii*hZ{pbYX-lOyv^@p)iC1|zaQYP_&Og4azob<(_fb8)#z_Bd6^m!F?6J$Udy2bT$I zRySoM>@rG|muaL!hYsR01mAZDCDKiCaq)6BemHksWNmG2ZBd(nA(=lwTXEYBa%}~X zZ;AjVDETSH#ATf7zyLXj%LfYhS0S=yJ8wlfsDE^h4d$}UO(?1^s|9X|4N8}K=gxMu zsSr#EahV52+*VXaDfxju#bqDr_~wC&(BY4)Tog(bm-8zxTWPk22>$|H+AY% zahb@$&_8u1TL7Z^vK_NJD27bChJ;?JR zBS$PSZp|2N5peQWe$q!QL{>Nn~BCncbb5DA)*;7_l zu3Ralr>9eg0;JM|5VXXdFp#aPvkRw|f&!f(-N+w4dGbW++qbXOty{NE0AioQue5vB z4`gIykVUBnw3-Q5Pz39vy2{+#+~g+fkX#{+Rh*x=Jc$9BQG)|QDte zL256J9XnPUJ$keUehJ$CrK%22kxD2h4+YPL-pk7?vy4UTY|9nd29;7-lwqh4{Bhn* z&Ob^mQ=yEvtgI>?0br*srKP1+sHC8}vNCVpJZbUbvYlMo0*O;9BJa(rQ|c?)X06Q3VYt~zWXlanB@no35_@}S+ZnYe0;oaFa6rJYw|sl z!D<~JH*VZ`EZ@7IefAlpv$cl~9n!)n%2Vsxq|)?%*O-`?ENT~aUVG=x9Z|1@-ega@ zfArBuBZ-#??`feB!CzsAblS9O?J!Vhwr}6A1*+7-ca?qo%9Sez-+ue;{?XCVx2Y}s zY9Bp%q^m=l%jjFXcklM4KF7w!%H=w+aq{Fz`MEPH@t_HE1W07W&is;8 z`Uf^uF0{i3$MhYvJX_W{DTeT|{Et{nIpC6MJk!2em zgC|X;Jrjk+Db1@)EINN zH;vz=y@K}qnG`%vQiJ5w=xI=end{=>A~`!d>yQ>ju+Y42OINMjzki>W^*8v)0`WSp zmMBHcVaJN1uiw&xoO-g!$zZ$s8k@po8BZ7H2~*^VNa?ASBpmy4utzp0URKwUl~Bm$ zJ6C2dTd-0KrSj*h8nS%1IF2jfcjfVvJ@-?9rWUMli6`8j%6wKiGXx)@rR60ne}FcD zzQnTp1?WPfdVg}%u3qPaS4ZciVu?!@78Wtq*4Bp+UkK+$ zbx{izENEqCXGhltM3+Z#`&dmbSxd~h3@e2wHN**|HV9uLwzL^iJu@AnFg|%l@$P9mFcS7dCF(NPp}Z=j7xRh#5^M4`zWZaZge4>7=A2+5{&B>;!uG z3j)6jm;ebFgmef?gE9qmyFn44cgbJ8_10TwF~h=fy@nJbl^#$yf_G1#Lz%3Q_oUK4 zg*Xg|Efiv+$U-FA+uPIkf8|v_CNwm(Hzq|A+B(3plGpY7;)^fFLY|wxzP@n>4jf2= z!bnCMuozg0xtQ6hUw-*zY)D8*ER=AC`n71$q6}Q0YuBz_Dz53k!VXMAIy8QD#*7(r z&{ihiyN*@e3w3mI@Ox4k?&&~!5QzF{E2*I;e3ZubR~2Q9lnk6&M^rkFf=`%()Ly-M z6<{eJS+;B$*?K}jE9h?lL8n0i9)AuKcnmWrsZ*y;_s^a^I{|gq`D930(i23-GS*?{ zg>uSc`l?l{f*c(kbAg_YqV5M)$RQ|%mMfjXGFBw@E&@6Il_$h-nHh@)g!jU;vyA3M z*$H?bymjl=6!^!C&6_uq!eT(=XAZP(-FholcM5n?Zrr#r+Q-Ky9g5rU=jZn$l$?L; z*scG#d{!-nM07QhcpM=+HFp@zZcUPL=hVVmo-Q@;S`FC3g{Nl8gD z0=6{FCNtPr7j~v6)lwmtk>rbPFcWXMySsP91bFo6r=OA&?}2x%I2zZg1d2MO2f0WA zIjq#Oy^mI{TD4%1a7rEu+`D)0Ay#@C2HFh4o)z=cuW@>MdUioU!9DmD3&=w$d)oN2 zgkqAh>XQJYHGt-mu_{avMAFF<4fxC%^<9Op^Pqje1`QhghPCz&?d`j}FW2fUpuYpe(hDBXO|o;^bW0+Qh$$o`f9 z3|PWsHiK+5lSS@@HagyekL!m4u*}KHSZpU2FeHchy$dz zFDxu90($Be5fNd7zR;Pj)aTEiC&RzA&CAO}b>UO2bYr0q{#^`b{R8^avs<@rxBs$h4A9<;9sSH%#tT&!IPJX(A&0ctHnUbp|QU#7TAs% zoCY)Y1U7RQZ2EMv+Zc3TEc-Cn*Jn^x9w_>Q4?ZAAy`P*M2KztB|Df$`;sK0I-LhrN zU!m|Y*y&IV@Co>toS8Fcy0QaY4WBcDpfz}oqdpVuM`A{1kPXLR(Vtg&$o_EO7G^O3 zip!z)VWyJMhe;$m24o5P9*^gRkn0h6!ZFL2Q>2Yak_b~10Vo{buV26X!Gi~%z~op& zzyp5e3I(p$u3ei1Q*|Gf?Kqx)0SXn_bwZbD;~Jht0;$ahoIeZSR{%M4(dHiu7cQKJ zx;^@EnT{FaAaiUdMJ$?_=g0g zW&ec0!NbGj4Vc65)2C1W9VTo8crU}8U1EUHK^_QlDighcEaWqF&3UBZ<=)%80!e@3YYN literal 0 HcmV?d00001 diff --git a/system/tests/test_data/views/test.css.php b/system/tests/test_data/views/test.css.php new file mode 100644 index 0000000..5c294dc --- /dev/null +++ b/system/tests/test_data/views/test.css.php @@ -0,0 +1 @@ +This is a view with a dot in the filename. \ No newline at end of file diff --git a/system/utf8/from_unicode.php b/system/utf8/from_unicode.php new file mode 100644 index 0000000..75471ae --- /dev/null +++ b/system/utf8/from_unicode.php @@ -0,0 +1,72 @@ += 0) AND ($arr[$k] <= 0x007f)) + { + echo chr($arr[$k]); + } + // 2 byte sequence + elseif ($arr[$k] <= 0x07ff) + { + echo chr(0xc0 | ($arr[$k] >> 6)); + echo chr(0x80 | ($arr[$k] & 0x003f)); + } + // Byte order mark (skip) + elseif ($arr[$k] == 0xFEFF) + { + // nop -- zap the BOM + } + // Test for illegal surrogates + elseif ($arr[$k] >= 0xD800 AND $arr[$k] <= 0xDFFF) + { + // Found a surrogate + throw new UTF8_Exception("UTF8::from_unicode: Illegal surrogate at index: ':index', value: ':value'", array( + ':index' => $k, + ':value' => $arr[$k], + )); + } + // 3 byte sequence + elseif ($arr[$k] <= 0xffff) + { + echo chr(0xe0 | ($arr[$k] >> 12)); + echo chr(0x80 | (($arr[$k] >> 6) & 0x003f)); + echo chr(0x80 | ($arr[$k] & 0x003f)); + } + // 4 byte sequence + elseif ($arr[$k] <= 0x10ffff) + { + echo chr(0xf0 | ($arr[$k] >> 18)); + echo chr(0x80 | (($arr[$k] >> 12) & 0x3f)); + echo chr(0x80 | (($arr[$k] >> 6) & 0x3f)); + echo chr(0x80 | ($arr[$k] & 0x3f)); + } + // Out of range + else + { + throw new UTF8_Exception("UTF8::from_unicode: Codepoint out of Unicode range at index: ':index', value: ':value'", array( + ':index' => $k, + ':value' => $arr[$k], + )); + } + } + + $result = ob_get_contents(); + ob_end_clean(); + return $result; +} diff --git a/system/utf8/ltrim.php b/system/utf8/ltrim.php new file mode 100644 index 0000000..c49e131 --- /dev/null +++ b/system/utf8/ltrim.php @@ -0,0 +1,22 @@ += 0 AND $ord0 <= 127) + return $ord0; + + if ( ! isset($chr[1])) + { + throw new UTF8_Exception('Short sequence - at least 2 bytes expected, only 1 seen'); + } + + $ord1 = ord($chr[1]); + + if ($ord0 >= 192 AND $ord0 <= 223) + return ($ord0 - 192) * 64 + ($ord1 - 128); + + if ( ! isset($chr[2])) + { + throw new UTF8_Exception('Short sequence - at least 3 bytes expected, only 2 seen'); + } + + $ord2 = ord($chr[2]); + + if ($ord0 >= 224 AND $ord0 <= 239) + return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128); + + if ( ! isset($chr[3])) + { + throw new UTF8_Exception('Short sequence - at least 4 bytes expected, only 3 seen'); + } + + $ord3 = ord($chr[3]); + + if ($ord0 >= 240 AND $ord0 <= 247) + return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2-128) * 64 + ($ord3 - 128); + + if ( ! isset($chr[4])) + { + throw new UTF8_Exception('Short sequence - at least 5 bytes expected, only 4 seen'); + } + + $ord4 = ord($chr[4]); + + if ($ord0 >= 248 AND $ord0 <= 251) + return ($ord0 - 248) * 16777216 + ($ord1-128) * 262144 + ($ord2 - 128) * 4096 + ($ord3 - 128) * 64 + ($ord4 - 128); + + if ( ! isset($chr[5])) + { + throw new UTF8_Exception('Short sequence - at least 6 bytes expected, only 5 seen'); + } + + if ($ord0 >= 252 AND $ord0 <= 253) + return ($ord0 - 252) * 1073741824 + ($ord1 - 128) * 16777216 + ($ord2 - 128) * 262144 + ($ord3 - 128) * 4096 + ($ord4 - 128) * 64 + (ord($chr[5]) - 128); + + if ($ord0 >= 254 AND $ord0 <= 255) + { + throw new UTF8_Exception("Invalid UTF-8 with surrogate ordinal ':ordinal'", array( + ':ordinal' => $ord0, + )); + } +} \ No newline at end of file diff --git a/system/utf8/rtrim.php b/system/utf8/rtrim.php new file mode 100644 index 0000000..9dfca8e --- /dev/null +++ b/system/utf8/rtrim.php @@ -0,0 +1,22 @@ + $val) + { + $str[$key] = UTF8::str_ireplace($search, $replace, $val, $count); + } + return $str; + } + + if (is_array($search)) + { + $keys = array_keys($search); + + foreach ($keys as $k) + { + if (is_array($replace)) + { + if (array_key_exists($k, $replace)) + { + $str = UTF8::str_ireplace($search[$k], $replace[$k], $str, $count); + } + else + { + $str = UTF8::str_ireplace($search[$k], '', $str, $count); + } + } + else + { + $str = UTF8::str_ireplace($search[$k], $replace, $str, $count); + } + } + return $str; + } + + $search = UTF8::strtolower($search); + $str_lower = UTF8::strtolower($str); + + $total_matched_strlen = 0; + $i = 0; + + while (preg_match('/(.*?)'.preg_quote($search, '/').'/s', $str_lower, $matches)) + { + $matched_strlen = strlen($matches[0]); + $str_lower = substr($str_lower, $matched_strlen); + + $offset = $total_matched_strlen + strlen($matches[1]) + ($i * (strlen($replace) - 1)); + $str = substr_replace($str, $replace, $offset, strlen($search)); + + $total_matched_strlen += $matched_strlen; + $i++; + } + + $count += $i; + return $str; +} diff --git a/system/utf8/str_pad.php b/system/utf8/str_pad.php new file mode 100644 index 0000000..1ef1187 --- /dev/null +++ b/system/utf8/str_pad.php @@ -0,0 +1,52 @@ + $pad_type, + )); +} \ No newline at end of file diff --git a/system/utf8/str_split.php b/system/utf8/str_split.php new file mode 100644 index 0000000..97cfa24 --- /dev/null +++ b/system/utf8/str_split.php @@ -0,0 +1,27 @@ +0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062, + 0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101, + 0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3, + 0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C, + 0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F, + 0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F, + 0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3, + 0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B, + 0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9, + 0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D, + 0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4, + 0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165, + 0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157, + 0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119, + 0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129, + 0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448, + 0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075, + 0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A, + 0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC, + 0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0, + 0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D, + 0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0, + 0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5, + 0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA, + 0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065, + 0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F, + 0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068, + 0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6, + 0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457, + 0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5, + 0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6, + 0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071, + 0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458, + 0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE, + 0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127, + 0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C, + 0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F, + 0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB, + 0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441, + 0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B, + 0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103, + 0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9, + 0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123, + ); + } + + $uni = UTF8::to_unicode($str); + + if ($uni === FALSE) + return FALSE; + + for ($i = 0, $c = count($uni); $i < $c; $i++) + { + if (isset($utf8_upper_to_lower[$uni[$i]])) + { + $uni[$i] = $utf8_upper_to_lower[$uni[$i]]; + } + } + + return UTF8::from_unicode($uni); +} \ No newline at end of file diff --git a/system/utf8/strtoupper.php b/system/utf8/strtoupper.php new file mode 100644 index 0000000..c3895db --- /dev/null +++ b/system/utf8/strtoupper.php @@ -0,0 +1,81 @@ +0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042, + 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100, + 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393, + 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C, + 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F, + 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E, + 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3, + 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A, + 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9, + 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C, + 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4, + 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164, + 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156, + 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118, + 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128, + 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428, + 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055, + 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A, + 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC, + 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0, + 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D, + 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0, + 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5, + 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA, + 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045, + 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F, + 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048, + 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6, + 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407, + 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395, + 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396, + 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051, + 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408, + 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F, + 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126, + 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C, + 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E, + 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB, + 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421, + 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A, + 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102, + 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9, + 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122, + ); + } + + $uni = UTF8::to_unicode($str); + + if ($uni === FALSE) + return FALSE; + + for ($i = 0, $c = count($uni); $i < $c; $i++) + { + if (isset($utf8_lower_to_upper[$uni[$i]])) + { + $uni[$i] = $utf8_lower_to_upper[$uni[$i]]; + } + } + + return UTF8::from_unicode($uni); +} \ No newline at end of file diff --git a/system/utf8/substr.php b/system/utf8/substr.php new file mode 100644 index 0000000..efb008d --- /dev/null +++ b/system/utf8/substr.php @@ -0,0 +1,72 @@ += $strlen OR ($length < 0 AND $length <= $offset - $strlen)) + return ''; + + // Whole string + if ($offset == 0 AND ($length === NULL OR $length >= $strlen)) + return $str; + + // Build regex + $regex = '^'; + + // Create an offset expression + if ($offset > 0) + { + // PCRE repeating quantifiers must be less than 65536, so repeat when necessary + $x = (int) ($offset / 65535); + $y = (int) ($offset % 65535); + $regex .= ($x == 0) ? '' : ('(?:.{65535}){'.$x.'}'); + $regex .= ($y == 0) ? '' : ('.{'.$y.'}'); + } + + // Create a length expression + if ($length === NULL) + { + $regex .= '(.*)'; // No length set, grab it all + } + // Find length from the left (positive length) + elseif ($length > 0) + { + // Reduce length so that it can't go beyond the end of the string + $length = min($strlen - $offset, $length); + + $x = (int) ($length / 65535); + $y = (int) ($length % 65535); + $regex .= '('; + $regex .= ($x == 0) ? '' : ('(?:.{65535}){'.$x.'}'); + $regex .= '.{'.$y.'})'; + } + // Find length from the right (negative length) + else + { + $x = (int) (-$length / 65535); + $y = (int) (-$length % 65535); + $regex .= '(.*)'; + $regex .= ($x == 0) ? '' : ('(?:.{65535}){'.$x.'}'); + $regex .= '.{'.$y.'}'; + } + + preg_match('/'.$regex.'/us', $str, $matches); + return $matches[1]; +} \ No newline at end of file diff --git a/system/utf8/substr_replace.php b/system/utf8/substr_replace.php new file mode 100644 index 0000000..494521f --- /dev/null +++ b/system/utf8/substr_replace.php @@ -0,0 +1,22 @@ + 0x10FFFF)) + { + trigger_error('UTF8::to_unicode: Illegal sequence or codepoint in UTF-8 at byte '.$i, E_USER_WARNING); + return FALSE; + } + + if (0xFEFF != $m_ucs4) + { + // BOM is legal but we don't want to output it + $out[] = $m_ucs4; + } + + // Initialize UTF-8 cache + $m_state = 0; + $m_ucs4 = 0; + $m_bytes = 1; + } + } + else + { + // ((0xC0 & (*in) != 0x80) AND (m_state != 0)) + // Incomplete multi-octet sequence + throw new UTF8_Exception("UTF8::to_unicode: Incomplete multi-octet sequence in UTF-8 at byte ':byte'", array( + ':byte' => $i, + )); + } + } + } + + return $out; +} \ No newline at end of file diff --git a/system/utf8/transliterate_to_ascii.php b/system/utf8/transliterate_to_ascii.php new file mode 100644 index 0000000..33dc05c --- /dev/null +++ b/system/utf8/transliterate_to_ascii.php @@ -0,0 +1,77 @@ + 'a', 'ô' => 'o', 'ď' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'š' => 's', 'ơ' => 'o', + 'ß' => 'ss', 'ă' => 'a', 'ř' => 'r', 'ț' => 't', 'ň' => 'n', 'ā' => 'a', 'ķ' => 'k', + 'ŝ' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'ṗ' => 'p', 'ó' => 'o', + 'ú' => 'u', 'ě' => 'e', 'é' => 'e', 'ç' => 'c', 'ẁ' => 'w', 'ċ' => 'c', 'õ' => 'o', + 'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c', + 'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't', + 'ū' => 'u', 'č' => 'c', 'ö' => 'o', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l', + 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z', + 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't', + 'ŗ' => 'r', 'ä' => 'a', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'u', 'ò' => 'o', + 'ē' => 'e', 'ñ' => 'n', 'ń' => 'n', 'ĥ' => 'h', 'ĝ' => 'g', 'đ' => 'd', 'ĵ' => 'j', + 'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o', + 'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g', + 'ṁ' => 'm', 'ō' => 'o', 'ĩ' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a', + 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'ĕ' => 'e', 'ı' => 'i', + ); + } + + $str = str_replace( + array_keys($utf8_lower_accents), + array_values($utf8_lower_accents), + $str + ); + } + + if ($case >= 0) + { + if ($utf8_upper_accents === NULL) + { + $utf8_upper_accents = array( + 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O', + 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K', 'Ĕ' => 'E', + 'Ŝ' => 'S', 'Ỳ' => 'Y', 'Ņ' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'Ṗ' => 'P', 'Ó' => 'O', + 'Ú' => 'U', 'Ě' => 'E', 'É' => 'E', 'Ç' => 'C', 'Ẁ' => 'W', 'Ċ' => 'C', 'Õ' => 'O', + 'Ṡ' => 'S', 'Ø' => 'O', 'Ģ' => 'G', 'Ŧ' => 'T', 'Ș' => 'S', 'Ė' => 'E', 'Ĉ' => 'C', + 'Ś' => 'S', 'Î' => 'I', 'Ű' => 'U', 'Ć' => 'C', 'Ę' => 'E', 'Ŵ' => 'W', 'Ṫ' => 'T', + 'Ū' => 'U', 'Č' => 'C', 'Ö' => 'O', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L', + 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z', + 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T', + 'Ŗ' => 'R', 'Ä' => 'A', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'U', 'Ò' => 'O', + 'Ē' => 'E', 'Ñ' => 'N', 'Ń' => 'N', 'Ĥ' => 'H', 'Ĝ' => 'G', 'Đ' => 'D', 'Ĵ' => 'J', + 'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Ţ' => 'T', 'Ý' => 'Y', 'Ő' => 'O', + 'Â' => 'A', 'Ľ' => 'L', 'Ẅ' => 'W', 'Ż' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ġ' => 'G', + 'Ṁ' => 'M', 'Ō' => 'O', 'Ĩ' => 'I', 'Ù' => 'U', 'Į' => 'I', 'Ź' => 'Z', 'Á' => 'A', + 'Û' => 'U', 'Þ' => 'Th', 'Ð' => 'Dh', 'Æ' => 'Ae', 'İ' => 'I', + ); + } + + $str = str_replace( + array_keys($utf8_upper_accents), + array_values($utf8_upper_accents), + $str + ); + } + + return $str; +} \ No newline at end of file diff --git a/system/utf8/trim.php b/system/utf8/trim.php new file mode 100644 index 0000000..9003ce0 --- /dev/null +++ b/system/utf8/trim.php @@ -0,0 +1,17 @@ + + + +
      +

      [ ]:

      +
      +

      [ ]

      + +
        + $step): ?> +
      1. +

        + + + [ ] + + {} + + + » + () +

        + + + + + + +
      2. + + +
      +
      +

      + +
      diff --git a/system/views/kohana/generate_logo.php b/system/views/kohana/generate_logo.php new file mode 100644 index 0000000..6b99f1d --- /dev/null +++ b/system/views/kohana/generate_logo.php @@ -0,0 +1,14 @@ + 'image/png', 'data' => '{$data}'); ?>"); \ No newline at end of file diff --git a/system/views/kohana/logo.php b/system/views/kohana/logo.php new file mode 100644 index 0000000..b19e68f --- /dev/null +++ b/system/views/kohana/logo.php @@ -0,0 +1,8 @@ + 'image/png', 'data' => 'iVBORw0KGgoAAAANSUhEUgAAAL8AAAA+CAYAAAB6Bsp7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACtppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMi4yLWMwNjMgNTMuMzUyNjI0LCAyMDA4LzA3LzMwLTE4OjA1OjQxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIKICAgeG1wUmlnaHRzOk1hcmtlZD0iRmFsc2UiCiAgIHhtcFJpZ2h0czpXZWJTdGF0ZW1lbnQ9IiI+CiAgIDxkYzpyaWdodHM+CiAgICA8cmRmOkFsdD4KICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiLz4KICAgIDwvcmRmOkFsdD4KICAgPC9kYzpyaWdodHM+CiAgIDx4bXBSaWdodHM6VXNhZ2VUZXJtcz4KICAgIDxyZGY6QWx0PgogICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCIvPgogICAgPC9yZGY6QWx0PgogICA8L3htcFJpZ2h0czpVc2FnZVRlcm1zPgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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'); ?> \ No newline at end of file diff --git a/system/views/profiler/stats.php b/system/views/profiler/stats.php new file mode 100644 index 0000000..f3e2868 --- /dev/null +++ b/system/views/profiler/stats.php @@ -0,0 +1,74 @@ + + + + + + +
      + $benchmarks): ?> + + + + + + + + + + + + + + + $tokens): ?> + + + + + + + + + + + + + +
      s
      kB
      +
      +
      s
      + +
      + +
      +
      +
      +
      kB
      + +
      + +
      +
      + + + + + + + + + + + + + + + +
      s
      kB
      +
      \ No newline at end of file diff --git a/system/views/profiler/style.css b/system/views/profiler/style.css new file mode 100644 index 0000000..e6af3a0 --- /dev/null +++ b/system/views/profiler/style.css @@ -0,0 +1,27 @@ +.kohana table.profiler { width: 99%; margin: 0 auto 1em; border-collapse: collapse; } +.kohana table.profiler th, +.kohana table.profiler td { padding: 0.2em 0.4em; background: #fff; border: solid 1px #999; border-width: 1px 0; text-align: left; font-weight: normal; font-size: 1em; color: #111; vertical-align: top; text-align: right; } +.kohana table.profiler th.name { text-align: left; } +.kohana table.profiler tr.group th { font-size: 1.4em; background: #222; color: #eee; border-color: #222; } +.kohana table.profiler tr.group td { background: #222; color: #777; border-color: #222; } +.kohana table.profiler tr.group td.time { padding-bottom: 0; } +.kohana table.profiler tr.headers th { text-transform: lowercase; font-variant: small-caps; background: #ddd; color: #777; } +.kohana table.profiler tr.mark th.name { width: 40%; font-size: 1.2em; background: #fff; vertical-align: middle; } +.kohana table.profiler tr.mark td { padding: 0; } +.kohana table.profiler tr.mark.final td { padding: 0.2em 0.4em; } +.kohana table.profiler tr.mark td > div { position: relative; padding: 0.2em 0.4em; } +.kohana table.profiler tr.mark td div.value { position: relative; z-index: 2; } +.kohana table.profiler tr.mark td div.graph { position: absolute; top: 0; bottom: 0; right: 0; left: 100%; background: #71bdf0; z-index: 1; } +.kohana table.profiler tr.mark.memory td div.graph { background: #acd4f0; } +.kohana table.profiler tr.mark td.current { background: #eddecc; } +.kohana table.profiler tr.mark td.min { background: #d2f1cb; } +.kohana table.profiler tr.mark td.max { background: #ead3cb; } +.kohana table.profiler tr.mark td.average { background: #ddd; } +.kohana table.profiler tr.mark td.total { background: #d0e3f0; } +.kohana table.profiler tr.time td { border-bottom: 0; font-weight: bold; } +.kohana table.profiler tr.memory td { border-top: 0; } +.kohana table.profiler tr.final th.name { background: #222; color: #fff; } +.kohana table.profiler abbr { border: 0; color: #777; font-weight: normal; } +.kohana table.profiler:hover tr.group td { color: #ccc; } +.kohana table.profiler:hover tr.mark td div.graph { background: #1197f0; } +.kohana table.profiler:hover tr.mark.memory td div.graph { background: #7cc1f0; } \ No newline at end of file diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..004ba3a --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + private $prefixes = array(); + private $fallbackDirs = array(); + private $useIncludePath = false; + private $classMap = array(); + + public function getPrefixes() + { + return $this->prefixes; + } + + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of classes + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function add($prefix, $paths) + { + if (!$prefix) { + foreach ((array) $paths as $path) { + $this->fallbackDirs[] = $path; + } + + return; + } + if (isset($this->prefixes[$prefix])) { + $this->prefixes[$prefix] = array_merge( + $this->prefixes[$prefix], + (array) $paths + ); + } else { + $this->prefixes[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + include $file; + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + + foreach ($this->fallbackDirs as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { + return $file; + } + + return $this->classMap[$class] = false; + } +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..fa36f16 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,351 @@ + $baseDir . '/vendor/phpunit/php-file-iterator/File/Iterator.php', + 'File_Iterator_Facade' => $baseDir . '/vendor/phpunit/php-file-iterator/File/Iterator/Facade.php', + 'File_Iterator_Factory' => $baseDir . '/vendor/phpunit/php-file-iterator/File/Iterator/Factory.php', + 'PHPUnit_Extensions_GroupTestSuite' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/GroupTestSuite.php', + 'PHPUnit_Extensions_PhptTestCase' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase.php', + 'PHPUnit_Extensions_PhptTestCase_Logger' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase/Logger.php', + 'PHPUnit_Extensions_PhptTestSuite' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestSuite.php', + 'PHPUnit_Extensions_RepeatedTest' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/RepeatedTest.php', + 'PHPUnit_Extensions_TestDecorator' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/TestDecorator.php', + 'PHPUnit_Extensions_TicketListener' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Extensions/TicketListener.php', + 'PHPUnit_Framework_Assert' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Assert.php', + 'PHPUnit_Framework_AssertionFailedError' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/AssertionFailedError.php', + 'PHPUnit_Framework_Comparator' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator.php', + 'PHPUnit_Framework_ComparatorFactory' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/ComparatorFactory.php', + 'PHPUnit_Framework_Comparator_Array' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Array.php', + 'PHPUnit_Framework_Comparator_DOMDocument' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/DOMDocument.php', + 'PHPUnit_Framework_Comparator_Double' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Double.php', + 'PHPUnit_Framework_Comparator_Exception' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Exception.php', + 'PHPUnit_Framework_Comparator_MockObject' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/MockObject.php', + 'PHPUnit_Framework_Comparator_Numeric' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Numeric.php', + 'PHPUnit_Framework_Comparator_Object' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Object.php', + 'PHPUnit_Framework_Comparator_Resource' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Resource.php', + 'PHPUnit_Framework_Comparator_Scalar' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Scalar.php', + 'PHPUnit_Framework_Comparator_SplObjectStorage' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/SplObjectStorage.php', + 'PHPUnit_Framework_Comparator_Type' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Type.php', + 'PHPUnit_Framework_ComparisonFailure' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/ComparisonFailure.php', + 'PHPUnit_Framework_Constraint' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint.php', + 'PHPUnit_Framework_Constraint_And' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/And.php', + 'PHPUnit_Framework_Constraint_ArrayHasKey' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ArrayHasKey.php', + 'PHPUnit_Framework_Constraint_Attribute' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Attribute.php', + 'PHPUnit_Framework_Constraint_Callback' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Callback.php', + 'PHPUnit_Framework_Constraint_ClassHasAttribute' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasAttribute.php', + 'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php', + 'PHPUnit_Framework_Constraint_Composite' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Composite.php', + 'PHPUnit_Framework_Constraint_Count' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Count.php', + 'PHPUnit_Framework_Constraint_Exception' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Exception.php', + 'PHPUnit_Framework_Constraint_ExceptionCode' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionCode.php', + 'PHPUnit_Framework_Constraint_ExceptionMessage' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionMessage.php', + 'PHPUnit_Framework_Constraint_FileExists' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/FileExists.php', + 'PHPUnit_Framework_Constraint_GreaterThan' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/GreaterThan.php', + 'PHPUnit_Framework_Constraint_IsAnything' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsAnything.php', + 'PHPUnit_Framework_Constraint_IsEmpty' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEmpty.php', + 'PHPUnit_Framework_Constraint_IsEqual' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEqual.php', + 'PHPUnit_Framework_Constraint_IsFalse' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsFalse.php', + 'PHPUnit_Framework_Constraint_IsIdentical' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsIdentical.php', + 'PHPUnit_Framework_Constraint_IsInstanceOf' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsInstanceOf.php', + 'PHPUnit_Framework_Constraint_IsNull' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsNull.php', + 'PHPUnit_Framework_Constraint_IsTrue' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsTrue.php', + 'PHPUnit_Framework_Constraint_IsType' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsType.php', + 'PHPUnit_Framework_Constraint_JsonMatches' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches.php', + 'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php', + 'PHPUnit_Framework_Constraint_LessThan' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/LessThan.php', + 'PHPUnit_Framework_Constraint_Not' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Not.php', + 'PHPUnit_Framework_Constraint_ObjectHasAttribute' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php', + 'PHPUnit_Framework_Constraint_Or' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Or.php', + 'PHPUnit_Framework_Constraint_PCREMatch' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/PCREMatch.php', + 'PHPUnit_Framework_Constraint_SameSize' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/SameSize.php', + 'PHPUnit_Framework_Constraint_StringContains' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringContains.php', + 'PHPUnit_Framework_Constraint_StringEndsWith' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringEndsWith.php', + 'PHPUnit_Framework_Constraint_StringMatches' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringMatches.php', + 'PHPUnit_Framework_Constraint_StringStartsWith' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringStartsWith.php', + 'PHPUnit_Framework_Constraint_TraversableContains' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContains.php', + 'PHPUnit_Framework_Constraint_TraversableContainsOnly' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php', + 'PHPUnit_Framework_Constraint_Xor' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Xor.php', + 'PHPUnit_Framework_Error' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Error.php', + 'PHPUnit_Framework_Error_Deprecated' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Deprecated.php', + 'PHPUnit_Framework_Error_Notice' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Notice.php', + 'PHPUnit_Framework_Error_Warning' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Warning.php', + 'PHPUnit_Framework_Exception' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Exception.php', + 'PHPUnit_Framework_ExpectationFailedException' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/ExpectationFailedException.php', + 'PHPUnit_Framework_IncompleteTest' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTest.php', + 'PHPUnit_Framework_IncompleteTestError' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTestError.php', + 'PHPUnit_Framework_MockObject_Builder_Identity' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Identity.php', + 'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php', + 'PHPUnit_Framework_MockObject_Builder_Match' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Match.php', + 'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php', + 'PHPUnit_Framework_MockObject_Builder_Namespace' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Namespace.php', + 'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php', + 'PHPUnit_Framework_MockObject_Builder_Stub' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Stub.php', + 'PHPUnit_Framework_MockObject_Generator' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator.php', + 'PHPUnit_Framework_MockObject_Invocation' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation.php', + 'PHPUnit_Framework_MockObject_InvocationMocker' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/InvocationMocker.php', + 'PHPUnit_Framework_MockObject_Invocation_Object' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Object.php', + 'PHPUnit_Framework_MockObject_Invocation_Static' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Static.php', + 'PHPUnit_Framework_MockObject_Invokable' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invokable.php', + 'PHPUnit_Framework_MockObject_Matcher' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher.php', + 'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php', + 'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php', + 'PHPUnit_Framework_MockObject_Matcher_Invocation' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Invocation.php', + 'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php', + 'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php', + 'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php', + 'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php', + 'PHPUnit_Framework_MockObject_Matcher_MethodName' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/MethodName.php', + 'PHPUnit_Framework_MockObject_Matcher_Parameters' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Parameters.php', + 'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php', + 'PHPUnit_Framework_MockObject_MockBuilder' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockBuilder.php', + 'PHPUnit_Framework_MockObject_MockObject' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockObject.php', + 'PHPUnit_Framework_MockObject_Stub' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub.php', + 'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php', + 'PHPUnit_Framework_MockObject_Stub_Exception' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Exception.php', + 'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php', + 'PHPUnit_Framework_MockObject_Stub_Return' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Return.php', + 'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php', + 'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php', + 'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php', + 'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php', + 'PHPUnit_Framework_MockObject_Verifiable' => $baseDir . '/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Verifiable.php', + 'PHPUnit_Framework_OutputError' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/OutputError.php', + 'PHPUnit_Framework_SelfDescribing' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/SelfDescribing.php', + 'PHPUnit_Framework_SkippedTest' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTest.php', + 'PHPUnit_Framework_SkippedTestError' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestError.php', + 'PHPUnit_Framework_SkippedTestSuiteError' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestSuiteError.php', + 'PHPUnit_Framework_SyntheticError' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/SyntheticError.php', + 'PHPUnit_Framework_Test' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Test.php', + 'PHPUnit_Framework_TestCase' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php', + 'PHPUnit_Framework_TestFailure' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/TestFailure.php', + 'PHPUnit_Framework_TestListener' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/TestListener.php', + 'PHPUnit_Framework_TestResult' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php', + 'PHPUnit_Framework_TestSuite' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php', + 'PHPUnit_Framework_TestSuite_DataProvider' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite/DataProvider.php', + 'PHPUnit_Framework_Warning' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Framework/Warning.php', + 'PHPUnit_Runner_BaseTestRunner' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Runner/BaseTestRunner.php', + 'PHPUnit_Runner_StandardTestSuiteLoader' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Runner/StandardTestSuiteLoader.php', + 'PHPUnit_Runner_TestSuiteLoader' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Runner/TestSuiteLoader.php', + 'PHPUnit_Runner_Version' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Runner/Version.php', + 'PHPUnit_TextUI_Command' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php', + 'PHPUnit_TextUI_ResultPrinter' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/TextUI/ResultPrinter.php', + 'PHPUnit_TextUI_TestRunner' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php', + 'PHPUnit_Util_Class' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Class.php', + 'PHPUnit_Util_Configuration' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Configuration.php', + 'PHPUnit_Util_DeprecatedFeature' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature.php', + 'PHPUnit_Util_DeprecatedFeature_Logger' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php', + 'PHPUnit_Util_Diff' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Diff.php', + 'PHPUnit_Util_ErrorHandler' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/ErrorHandler.php', + 'PHPUnit_Util_Fileloader' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Fileloader.php', + 'PHPUnit_Util_Filesystem' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Filesystem.php', + 'PHPUnit_Util_Filter' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Filter.php', + 'PHPUnit_Util_Getopt' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Getopt.php', + 'PHPUnit_Util_GlobalState' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php', + 'PHPUnit_Util_InvalidArgumentHelper' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/InvalidArgumentHelper.php', + 'PHPUnit_Util_Log_JSON' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Log/JSON.php', + 'PHPUnit_Util_Log_JUnit' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php', + 'PHPUnit_Util_Log_TAP' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Log/TAP.php', + 'PHPUnit_Util_PHP' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/PHP.php', + 'PHPUnit_Util_PHP_Default' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Default.php', + 'PHPUnit_Util_PHP_Windows' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Windows.php', + 'PHPUnit_Util_Printer' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Printer.php', + 'PHPUnit_Util_String' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/String.php', + 'PHPUnit_Util_Test' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Test.php', + 'PHPUnit_Util_TestDox_NamePrettifier' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/NamePrettifier.php', + 'PHPUnit_Util_TestDox_ResultPrinter' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter.php', + 'PHPUnit_Util_TestDox_ResultPrinter_HTML' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php', + 'PHPUnit_Util_TestDox_ResultPrinter_Text' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/Text.php', + 'PHPUnit_Util_TestSuiteIterator' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/TestSuiteIterator.php', + 'PHPUnit_Util_Type' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/Type.php', + 'PHPUnit_Util_XML' => $baseDir . '/vendor/phpunit/phpunit/PHPUnit/Util/XML.php', + 'PHP_CodeCoverage' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage.php', + 'PHP_CodeCoverage_Driver' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver.php', + 'PHP_CodeCoverage_Driver_Xdebug' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver/Xdebug.php', + 'PHP_CodeCoverage_Exception' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Exception.php', + 'PHP_CodeCoverage_Filter' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Filter.php', + 'PHP_CodeCoverage_Report_Clover' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Clover.php', + 'PHP_CodeCoverage_Report_Factory' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Factory.php', + 'PHP_CodeCoverage_Report_HTML' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML.php', + 'PHP_CodeCoverage_Report_HTML_Renderer' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer.php', + 'PHP_CodeCoverage_Report_HTML_Renderer_Dashboard' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php', + 'PHP_CodeCoverage_Report_HTML_Renderer_Directory' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php', + 'PHP_CodeCoverage_Report_HTML_Renderer_File' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/File.php', + 'PHP_CodeCoverage_Report_Node' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node.php', + 'PHP_CodeCoverage_Report_Node_Directory' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Directory.php', + 'PHP_CodeCoverage_Report_Node_File' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php', + 'PHP_CodeCoverage_Report_Node_Iterator' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Iterator.php', + 'PHP_CodeCoverage_Report_PHP' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/PHP.php', + 'PHP_CodeCoverage_Report_Text' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Text.php', + 'PHP_CodeCoverage_Util' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util.php', + 'PHP_CodeCoverage_Util_InvalidArgumentHelper' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util/InvalidArgumentHelper.php', + 'PHP_CodeCoverage_Version' => $baseDir . '/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Version.php', + 'PHP_Timer' => $baseDir . '/vendor/phpunit/php-timer/PHP/Timer.php', + 'PHP_Token' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_TokenWithScope' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_TokenWithScopeAndVisibility' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ABSTRACT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_AMPERSAND' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_AND_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ARRAY' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ARRAY_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_AS' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_AT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_BACKTICK' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_BAD_CHARACTER' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_BOOLEAN_AND' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_BOOLEAN_OR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_BOOL_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_BREAK' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CALLABLE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CARET' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CASE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CATCH' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CHARACTER' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLASS' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLASS_C' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLONE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLOSE_BRACKET' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLOSE_CURLY' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLOSE_SQUARE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CLOSE_TAG' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_COLON' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_COMMA' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_COMMENT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CONCAT_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CONST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CONSTANT_ENCAPSED_STRING' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CONTINUE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_CURLY_OPEN' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DEC' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DECLARE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DEFAULT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DIR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DIV' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DIV_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DNUMBER' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DO' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOC_COMMENT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOLLAR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOUBLE_ARROW' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOUBLE_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOUBLE_COLON' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_DOUBLE_QUOTES' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ECHO' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ELSE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ELSEIF' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_EMPTY' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENCAPSED_AND_WHITESPACE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENDDECLARE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENDFOR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENDFOREACH' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENDIF' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENDSWITCH' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ENDWHILE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_END_HEREDOC' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_EVAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_EXCLAMATION_MARK' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_EXIT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_EXTENDS' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_FILE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_FINAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_FOR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_FOREACH' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_FUNCTION' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_FUNC_C' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_GLOBAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_GOTO' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_GT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_HALT_COMPILER' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IF' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IMPLEMENTS' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INC' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INCLUDE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INCLUDE_ONCE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INLINE_HTML' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INSTANCEOF' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INSTEADOF' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INTERFACE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_INT_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_ISSET' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IS_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IS_GREATER_OR_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IS_IDENTICAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IS_NOT_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IS_NOT_IDENTICAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_IS_SMALLER_OR_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_Includes' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LINE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LIST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LNUMBER' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LOGICAL_AND' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LOGICAL_OR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LOGICAL_XOR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_LT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_METHOD_C' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_MINUS' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_MINUS_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_MOD_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_MULT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_MUL_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_NAMESPACE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_NEW' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_NS_C' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_NS_SEPARATOR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_NUM_STRING' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OBJECT_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OBJECT_OPERATOR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OPEN_BRACKET' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OPEN_CURLY' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OPEN_SQUARE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OPEN_TAG' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OPEN_TAG_WITH_ECHO' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_OR_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PERCENT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PIPE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PLUS' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PLUS_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PRINT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PRIVATE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PROTECTED' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_PUBLIC' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_QUESTION_MARK' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_REQUIRE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_REQUIRE_ONCE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_RETURN' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_SEMICOLON' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_SL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_SL_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_SR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_SR_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_START_HEREDOC' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_STATIC' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_STRING' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_STRING_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_STRING_VARNAME' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_SWITCH' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_Stream' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token/Stream.php', + 'PHP_Token_Stream_CachingFactory' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token/Stream/CachingFactory.php', + 'PHP_Token_THROW' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_TILDE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_TRAIT' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_TRAIT_C' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_TRY' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_UNSET' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_UNSET_CAST' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_USE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_VAR' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_VARIABLE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_WHILE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_WHITESPACE' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'PHP_Token_XOR_EQUAL' => $baseDir . '/vendor/phpunit/php-token-stream/PHP/Token.php', + 'Text_Template' => $baseDir . '/vendor/phpunit/php-text-template/Text/Template.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..851f89d --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,10 @@ + $vendorDir . '/symfony/yaml/', +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..dc7e257 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,39 @@ + $path) { + $loader->add($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(); + + return $loader; + } +} diff --git a/vendor/composer/include_paths.php b/vendor/composer/include_paths.php new file mode 100644 index 0000000..533f1c8 --- /dev/null +++ b/vendor/composer/include_paths.php @@ -0,0 +1,17 @@ +=5.3.3" + }, + "time": "2012-08-22 06:48:41", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml": "" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com" + }, + { + "name": "phpunit/php-text-template", + "version": "1.1.3", + "version_normalized": "1.1.3.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-text-template.git", + "reference": "1.1.3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.3", + "reference": "1.1.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:48:39", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "template" + ] + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.1", + "version_normalized": "1.2.1.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "1.2.1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/zipball/1.2.1", + "reference": "1.2.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable", + "ext-reflection": "*", + "ext-spl": "*" + }, + "suggest": { + "ext-soap": "*" + }, + "time": "2012-10-05 00:00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "mock", + "xunit" + ] + }, + { + "name": "phpunit/php-timer", + "version": "1.0.4", + "version_normalized": "1.0.4.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-timer.git", + "reference": "1.0.4" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4", + "reference": "1.0.4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:45:58", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "timer" + ] + }, + { + "name": "phpunit/php-token-stream", + "version": "1.1.5", + "version_normalized": "1.1.5.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-token-stream.git", + "reference": "1.1.5" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5", + "reference": "1.1.5", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:47:14", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "tokenizer" + ] + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.3", + "version_normalized": "1.3.3.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "1.3.3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3", + "reference": "1.3.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-10-11 04:44:38", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "File/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "filesystem", + "iterator" + ] + }, + { + "name": "phpunit/php-code-coverage", + "version": "1.2.6", + "version_normalized": "1.2.6.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1.2.6" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-code-coverage/zipball/1.2.6", + "reference": "1.2.6", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-token-stream": ">=1.1.3@stable", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "time": "2012-10-16 22:34:13", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "testing", + "coverage", + "xunit" + ] + }, + { + "name": "phpunit/phpunit", + "version": "3.7.8", + "version_normalized": "3.7.8.0", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/phpunit.git", + "reference": "3.7.8" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/phpunit/zipball/3.7.8", + "reference": "3.7.8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-code-coverage": ">=1.2.1", + "phpunit/php-timer": ">=1.0.2", + "phpunit/phpunit-mock-objects": ">=1.2.0", + "symfony/yaml": ">=2.1.0", + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*" + }, + "suggest": { + "phpunit/php-invoker": ">=1.1.0", + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*" + }, + "time": "2012-10-16 22:37:08", + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "testing", + "phpunit", + "xunit" + ] + } +] diff --git a/vendor/phpunit/php-code-coverage/CONTRIBUTING.md b/vendor/phpunit/php-code-coverage/CONTRIBUTING.md new file mode 100644 index 0000000..b290539 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/CONTRIBUTING.md @@ -0,0 +1,5 @@ +Pull Requests for bug fixes should be made against the current release branch (1.2). + +Pull Requests for new features should be made against master. + +For further notes please refer to [https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md) diff --git a/vendor/phpunit/php-code-coverage/ChangeLog.markdown b/vendor/phpunit/php-code-coverage/ChangeLog.markdown new file mode 100644 index 0000000..c45ce6b --- /dev/null +++ b/vendor/phpunit/php-code-coverage/ChangeLog.markdown @@ -0,0 +1,46 @@ +PHP_CodeCoverage 1.2 +==================== + +This is the list of changes for the PHP_CodeCoverage 1.2 release series. + +PHP_CodeCoverage 1.2.6 +---------------------- +* Fixed #126: `E_NOTICE` thrown when generating coverage report. + +PHP_CodeCoverage 1.2.5 +---------------------- +* Fixed regression introduced in PHP_CodeCoverage 1.2.4. + +PHP_CodeCoverage 1.2.4 +---------------------- +* Fixed #123: Incorrect code coverage for interfaces. + +PHP_CodeCoverage 1.2.3 +---------------------- + +* Implemented #116: Do not rely on autoloader class map to populate blacklist. +* Added support for parentheses after method names in the `@covers` annotation. +* When `addUncoveredFilesFromWhitelist=FALSE` is set then files that are whitelisted but not covered by a single test are now excluded from the code coverage. +* Fixed #81: Non-english locales broke the coverage bars in the HTML report. +* Fixed #118: Percentage for tested classes and traits displayed incorrectly. +* Fixed #121: One line `@covers` annotations did not work. + +PHP_CodeCoverage 1.2.2 +---------------------- + +* Fixed #115: Backwards compatibility wrapper for `trait_exists()` does not work. + +PHP_CodeCoverage 1.2.1 +---------------------- + +* Fixed invalid markup in the HTML report. +* The version number is now displayed when using PHP_CodeCoverage from a Composer install or Git checkout. + +PHP_CodeCoverage 1.2.0 +---------------------- + +* The HTML report has been redesigned. +* The new `@coversDefaultClass` annotation enables short `@covers` annotations when working with long class names or namespaces. +* The new `@coversNothing` annotation can be used so tests do not record any code coverage. Useful for integration testing. +* When `processUncoveredFilesFromWhitelist=FALSE` is set then files that are whitelisted but not covered by a single test are now included in the code coverage but with all lines, including those that are not executable, counted as not executed. +* PHP_CodeCoverage 1.2 is only supported on PHP 5.3.3 (or later) and PHP 5.4.7 (or later) is highly recommended. diff --git a/vendor/phpunit/php-code-coverage/LICENSE b/vendor/phpunit/php-code-coverage/LICENSE new file mode 100644 index 0000000..3d72de9 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/LICENSE @@ -0,0 +1,33 @@ +PHP_CodeCoverage + +Copyright (c) 2009-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage.php new file mode 100644 index 0000000..37de2c1 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage.php @@ -0,0 +1,796 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +// @codeCoverageIgnoreStart +// @codingStandardsIgnoreStart +/** + * @SuppressWarnings(PHPMD) + */ +if (!function_exists('trait_exists')) { + function trait_exists($name) + { + return FALSE; + } +} +// @codingStandardsIgnoreEnd +// @codeCoverageIgnoreEnd + +/** + * Provides collection functionality for PHP code coverage information. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage +{ + /** + * @var PHP_CodeCoverage_Driver + */ + protected $driver; + + /** + * @var PHP_CodeCoverage_Filter + */ + protected $filter; + + /** + * @var boolean + */ + protected $cacheTokens = FALSE; + + /** + * @var boolean + */ + protected $forceCoversAnnotation = FALSE; + + /** + * @var boolean + */ + protected $mapTestClassNameToCoveredClassName = FALSE; + + /** + * @var boolean + */ + protected $addUncoveredFilesFromWhitelist = TRUE; + + /** + * @var boolean + */ + protected $processUncoveredFilesFromWhitelist = FALSE; + + /** + * @var mixed + */ + protected $currentId; + + /** + * Code coverage data. + * + * @var array + */ + protected $data = array(); + + /** + * Test data. + * + * @var array + */ + protected $tests = array(); + + /** + * Constructor. + * + * @param PHP_CodeCoverage_Driver $driver + * @param PHP_CodeCoverage_Filter $filter + */ + public function __construct(PHP_CodeCoverage_Driver $driver = NULL, PHP_CodeCoverage_Filter $filter = NULL) + { + if ($driver === NULL) { + $driver = new PHP_CodeCoverage_Driver_Xdebug; + } + + if ($filter === NULL) { + $filter = new PHP_CodeCoverage_Filter; + } + + $this->driver = $driver; + $this->filter = $filter; + } + + /** + * Returns the PHP_CodeCoverage_Report_Node_* object graph + * for this PHP_CodeCoverage object. + * + * @return PHP_CodeCoverage_Report_Node_Directory + * @since Method available since Release 1.1.0 + */ + public function getReport() + { + $factory = new PHP_CodeCoverage_Report_Factory; + + return $factory->create($this); + } + + /** + * Clears collected code coverage data. + */ + public function clear() + { + $this->currentId = NULL; + $this->data = array(); + $this->tests = array(); + } + + /** + * Returns the PHP_CodeCoverage_Filter used. + * + * @return PHP_CodeCoverage_Filter + */ + public function filter() + { + return $this->filter; + } + + /** + * Returns the collected code coverage data. + * + * @return array + * @since Method available since Release 1.1.0 + */ + public function getData() + { + if ($this->addUncoveredFilesFromWhitelist) { + $this->addUncoveredFilesFromWhitelist(); + } + + // We need to apply the blacklist filter a second time + // when no whitelist is used. + if (!$this->filter->hasWhitelist()) { + $this->applyListsFilter($this->data); + } + + return $this->data; + } + + /** + * Returns the test data. + * + * @return array + * @since Method available since Release 1.1.0 + */ + public function getTests() + { + return $this->tests; + } + + /** + * Start collection of code coverage information. + * + * @param mixed $id + * @param boolean $clear + * @throws PHP_CodeCoverage_Exception + */ + public function start($id, $clear = FALSE) + { + if (!is_bool($clear)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + if ($clear) { + $this->clear(); + } + + $this->currentId = $id; + + $this->driver->start(); + } + + /** + * Stop collection of code coverage information. + * + * @param boolean $append + * @return array + * @throws PHP_CodeCoverage_Exception + */ + public function stop($append = TRUE) + { + if (!is_bool($append)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $data = $this->driver->stop(); + $this->append($data, NULL, $append); + + $this->currentId = NULL; + + return $data; + } + + /** + * Appends code coverage data. + * + * @param array $data + * @param mixed $id + * @param boolean $append + */ + public function append(array $data, $id = NULL, $append = TRUE) + { + if ($id === NULL) { + $id = $this->currentId; + } + + if ($id === NULL) { + throw new PHP_CodeCoverage_Exception; + } + + $this->applyListsFilter($data); + $this->initializeFilesThatAreSeenTheFirstTime($data); + + if (!$append) { + return; + } + + if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') { + $this->applyCoversAnnotationFilter($data, $id); + } + + if (empty($data)) { + return; + } + + $status = NULL; + + if ($id instanceof PHPUnit_Framework_TestCase) { + $status = $id->getStatus(); + $id = get_class($id) . '::' . $id->getName(); + } + + else if ($id instanceof PHPUnit_Extensions_PhptTestCase) { + $id = $id->getName(); + } + + $this->tests[$id] = $status; + + foreach ($data as $file => $lines) { + if (!$this->filter->isFile($file)) { + continue; + } + + foreach ($lines as $k => $v) { + if ($v == 1) { + $this->data[$file][$k][] = $id; + } + } + } + } + + /** + * Merges the data from another instance of PHP_CodeCoverage. + * + * @param PHP_CodeCoverage $that + */ + public function merge(PHP_CodeCoverage $that) + { + foreach ($that->data as $file => $lines) { + if (!isset($this->data[$file])) { + if (!$this->filter->isFiltered($file)) { + $this->data[$file] = $lines; + } + + continue; + } + + foreach ($lines as $line => $data) { + if ($data !== NULL) { + if (!isset($this->data[$file][$line])) { + $this->data[$file][$line] = $data; + } else { + $this->data[$file][$line] = array_unique( + array_merge($this->data[$file][$line], $data) + ); + } + } + } + } + + $this->tests = array_merge($this->tests, $that->getTests()); + } + + /** + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception + * @since Method available since Release 1.1.0 + */ + public function setCacheTokens($flag) + { + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $this->cacheTokens = $flag; + } + + /** + * @param boolean $flag + * @since Method available since Release 1.1.0 + */ + public function getCacheTokens() + { + return $this->cacheTokens; + } + + /** + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception + */ + public function setForceCoversAnnotation($flag) + { + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $this->forceCoversAnnotation = $flag; + } + + /** + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception + */ + public function setMapTestClassNameToCoveredClassName($flag) + { + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $this->mapTestClassNameToCoveredClassName = $flag; + } + + /** + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception + */ + public function setAddUncoveredFilesFromWhitelist($flag) + { + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $this->addUncoveredFilesFromWhitelist = $flag; + } + + /** + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception + */ + public function setProcessUncoveredFilesFromWhitelist($flag) + { + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $this->processUncoveredFilesFromWhitelist = $flag; + } + + /** + * Applies the @covers annotation filtering. + * + * @param array $data + * @param mixed $id + */ + protected function applyCoversAnnotationFilter(&$data, $id) + { + if ($id instanceof PHPUnit_Framework_TestCase) { + $testClassName = get_class($id); + $linesToBeCovered = $this->getLinesToBeCovered( + $testClassName, $id->getName() + ); + + if ($this->mapTestClassNameToCoveredClassName && + empty($linesToBeCovered)) { + $testedClass = substr($testClassName, 0, -4); + + if (class_exists($testedClass)) { + $class = new ReflectionClass($testedClass); + + $linesToBeCovered = array( + $class->getFileName() => range( + $class->getStartLine(), $class->getEndLine() + ) + ); + } + } + } else { + $linesToBeCovered = array(); + } + + if (!empty($linesToBeCovered)) { + $data = array_intersect_key($data, $linesToBeCovered); + + foreach (array_keys($data) as $filename) { + $data[$filename] = array_intersect_key( + $data[$filename], array_flip($linesToBeCovered[$filename]) + ); + } + } + + else if ($this->forceCoversAnnotation) { + $data = array(); + } + } + + /** + * Applies the blacklist/whitelist filtering. + * + * @param array $data + */ + protected function applyListsFilter(&$data) + { + foreach (array_keys($data) as $filename) { + if ($this->filter->isFiltered($filename)) { + unset($data[$filename]); + } + } + } + + /** + * @since Method available since Release 1.1.0 + */ + protected function initializeFilesThatAreSeenTheFirstTime($data) + { + foreach ($data as $file => $lines) { + if ($this->filter->isFile($file) && !isset($this->data[$file])) { + $this->data[$file] = array(); + + foreach ($lines as $k => $v) { + $this->data[$file][$k] = $v == -2 ? NULL : array(); + } + } + } + } + + /** + * Processes whitelisted files that are not covered. + */ + protected function addUncoveredFilesFromWhitelist() + { + $data = array(); + $uncoveredFiles = array_diff( + $this->filter->getWhitelist(), array_keys($this->data) + ); + + foreach ($uncoveredFiles as $uncoveredFile) { + if (!file_exists($uncoveredFile)) { + continue; + } + + if ($this->processUncoveredFilesFromWhitelist) { + $this->processUncoveredFileFromWhitelist( + $uncoveredFile, $data, $uncoveredFiles + ); + } else { + $data[$uncoveredFile] = array(); + + $lines = count(file($uncoveredFile)); + + for ($i = 1; $i <= $lines; $i++) { + $data[$uncoveredFile][$i] = -1; + } + } + } + + $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + } + + /** + * @param string $uncoveredFile + * @param array $data + * @param array $uncoveredFiles + */ + protected function processUncoveredFileFromWhitelist($uncoveredFile, array &$data, array $uncoveredFiles) + { + $this->driver->start(); + include_once $uncoveredFile; + $coverage = $this->driver->stop(); + + foreach ($coverage as $file => $fileCoverage) { + if (!isset($data[$file]) && + in_array($file, $uncoveredFiles)) { + foreach (array_keys($fileCoverage) as $key) { + if ($fileCoverage[$key] == 1) { + $fileCoverage[$key] = -1; + } + } + + $data[$file] = $fileCoverage; + } + } + } + + /** + * Returns the files and lines a test method wants to cover. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 1.2.0 + */ + protected function getLinesToBeCovered($className, $methodName) + { + $codeToCoverList = array(); + $result = array(); + + // @codeCoverageIgnoreStart + if (($pos = strpos($methodName, ' ')) !== FALSE) { + $methodName = substr($methodName, 0, $pos); + } + // @codeCoverageIgnoreEnd + + $class = new ReflectionClass($className); + + try { + $method = new ReflectionMethod($className, $methodName); + } + + catch (ReflectionException $e) { + return array(); + } + + $docComment = substr($class->getDocComment(), 3, -2) . PHP_EOL . substr($method->getDocComment(), 3, -2); + + $templateMethods = array( + 'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown' + ); + + foreach ($templateMethods as $templateMethod) { + if ($class->hasMethod($templateMethod)) { + $reflector = $class->getMethod($templateMethod); + $docComment .= PHP_EOL . substr($reflector->getDocComment(), 3, -2); + unset($reflector); + } + } + + if (strpos($docComment, '@coversNothing') !== FALSE) { + return $result; + } + + $classShortcut = preg_match_all( + '(@coversDefaultClass\s+(?P.*?)\s*$)m', + $class->getDocComment(), + $matches + ); + + if ($classShortcut) { + if ($classShortcut > 1) { + throw new PHP_CodeCoverage_Exception( + sprintf( + 'More than one @coversClass annotation in class or interface "%s".', + $className + ) + ); + } + + $classShortcut = $matches['coveredClass'][0]; + } + + $match = preg_match_all( + '(@covers\s+(?P.*?)\s*(\(\s*\))?\s*$)m', + $docComment, + $matches + ); + + if ($match) { + foreach ($matches['coveredElement'] as $coveredElement) { + if ($classShortcut && strncmp($coveredElement, '::', 2) === 0) { + $coveredElement = $classShortcut . $coveredElement; + } + + $codeToCoverList = array_merge( + $codeToCoverList, + $this->resolveCoversToReflectionObjects($coveredElement) + ); + } + + foreach ($codeToCoverList as $codeToCover) { + $fileName = $codeToCover->getFileName(); + + if (!isset($result[$fileName])) { + $result[$fileName] = array(); + } + + $result[$fileName] = array_unique( + array_merge( + $result[$fileName], + range( + $codeToCover->getStartLine(), $codeToCover->getEndLine() + ) + ) + ); + } + } + + return $result; + } + + /** + * @param string $coveredElement + * @return array + * @since Method available since Release 1.2.0 + */ + protected function resolveCoversToReflectionObjects($coveredElement) + { + $codeToCoverList = array(); + + if (strpos($coveredElement, '::') !== FALSE) { + list($className, $methodName) = explode('::', $coveredElement); + + if (isset($methodName[0]) && $methodName[0] == '<') { + $classes = array($className); + + foreach ($classes as $className) { + if (!class_exists($className) && + !interface_exists($className)) { + throw new PHP_CodeCoverage_Exception( + sprintf( + 'Trying to @cover not existing class or ' . + 'interface "%s".', + $className + ) + ); + } + + $class = new ReflectionClass($className); + $methods = $class->getMethods(); + $inverse = isset($methodName[1]) && $methodName[1] == '!'; + + if (strpos($methodName, 'protected')) { + $visibility = 'isProtected'; + } + + else if (strpos($methodName, 'private')) { + $visibility = 'isPrivate'; + } + + else if (strpos($methodName, 'public')) { + $visibility = 'isPublic'; + } + + foreach ($methods as $method) { + if ($inverse && !$method->$visibility()) { + $codeToCoverList[] = $method; + } + + else if (!$inverse && $method->$visibility()) { + $codeToCoverList[] = $method; + } + } + } + } else { + $classes = array($className); + + foreach ($classes as $className) { + if ($className == '' && function_exists($methodName)) { + $codeToCoverList[] = new ReflectionFunction( + $methodName + ); + } else { + if (!((class_exists($className) || + interface_exists($className) || + trait_exists($className)) && + method_exists($className, $methodName))) { + throw new PHP_CodeCoverage_Exception( + sprintf( + 'Trying to @cover not existing method "%s::%s".', + $className, + $methodName + ) + ); + } + + $codeToCoverList[] = new ReflectionMethod( + $className, $methodName + ); + } + } + } + } else { + $extended = FALSE; + + if (strpos($coveredElement, '') !== FALSE) { + $coveredElement = str_replace( + '', '', $coveredElement + ); + + $extended = TRUE; + } + + $classes = array($coveredElement); + + if ($extended) { + $classes = array_merge( + $classes, + class_implements($coveredElement), + class_parents($coveredElement) + ); + } + + foreach ($classes as $className) { + if (!class_exists($className) && + !interface_exists($className) && + !trait_exists($className)) { + throw new PHP_CodeCoverage_Exception( + sprintf( + 'Trying to @cover not existing class or ' . + 'interface "%s".', + $className + ) + ); + } + + $codeToCoverList[] = new ReflectionClass($className); + } + } + + return $codeToCoverList; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php new file mode 100644 index 0000000..7bfa231 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php @@ -0,0 +1,90 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +require_once 'File/Iterator/Autoload.php'; +require_once 'PHP/Token/Stream/Autoload.php'; +require_once 'Text/Template/Autoload.php'; + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + 'php_codecoverage' => '/CodeCoverage.php', + 'php_codecoverage_driver' => '/CodeCoverage/Driver.php', + 'php_codecoverage_driver_xdebug' => '/CodeCoverage/Driver/Xdebug.php', + 'php_codecoverage_exception' => '/CodeCoverage/Exception.php', + 'php_codecoverage_filter' => '/CodeCoverage/Filter.php', + 'php_codecoverage_report_clover' => '/CodeCoverage/Report/Clover.php', + 'php_codecoverage_report_factory' => '/CodeCoverage/Report/Factory.php', + 'php_codecoverage_report_html' => '/CodeCoverage/Report/HTML.php', + 'php_codecoverage_report_html_renderer' => '/CodeCoverage/Report/HTML/Renderer.php', + 'php_codecoverage_report_html_renderer_dashboard' => '/CodeCoverage/Report/HTML/Renderer/Dashboard.php', + 'php_codecoverage_report_html_renderer_directory' => '/CodeCoverage/Report/HTML/Renderer/Directory.php', + 'php_codecoverage_report_html_renderer_file' => '/CodeCoverage/Report/HTML/Renderer/File.php', + 'php_codecoverage_report_node' => '/CodeCoverage/Report/Node.php', + 'php_codecoverage_report_node_directory' => '/CodeCoverage/Report/Node/Directory.php', + 'php_codecoverage_report_node_file' => '/CodeCoverage/Report/Node/File.php', + 'php_codecoverage_report_node_iterator' => '/CodeCoverage/Report/Node/Iterator.php', + 'php_codecoverage_report_php' => '/CodeCoverage/Report/PHP.php', + 'php_codecoverage_report_text' => '/CodeCoverage/Report/Text.php', + 'php_codecoverage_util' => '/CodeCoverage/Util.php', + 'php_codecoverage_util_invalidargumenthelper' => '/CodeCoverage/Util/InvalidArgumentHelper.php', + 'php_codecoverage_version' => '/CodeCoverage/Version.php' + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php.in b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php.in new file mode 100644 index 0000000..dc34395 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Autoload.php.in @@ -0,0 +1,70 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +require_once 'File/Iterator/Autoload.php'; +require_once 'PHP/Token/Stream/Autoload.php'; +require_once 'Text/Template/Autoload.php'; + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver.php new file mode 100644 index 0000000..0ccdad7 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver.php @@ -0,0 +1,70 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Interface for code coverage drivers. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +interface PHP_CodeCoverage_Driver +{ + /** + * Start collection of code coverage information. + */ + public function start(); + + /** + * Stop collection of code coverage information. + * + * @return array + */ + public function stop(); +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver/Xdebug.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver/Xdebug.php new file mode 100644 index 0000000..a2704f8 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Driver/Xdebug.php @@ -0,0 +1,97 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Driver for Xdebug's code coverage functionality. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + * @codeCoverageIgnore + */ +class PHP_CodeCoverage_Driver_Xdebug implements PHP_CodeCoverage_Driver +{ + /** + * Constructor. + */ + public function __construct() + { + if (!extension_loaded('xdebug')) { + throw new PHP_CodeCoverage_Exception('Xdebug is not loaded.'); + } + + if (version_compare(phpversion('xdebug'), '2.2.0-dev', '>=') && + !ini_get('xdebug.coverage_enable')) { + throw new PHP_CodeCoverage_Exception( + 'You need to set xdebug.coverage_enable=On in your php.ini.' + ); + } + } + + /** + * Start collection of code coverage information. + */ + public function start() + { + xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); + } + + /** + * Stop collection of code coverage information. + * + * @return array + */ + public function stop() + { + $codeCoverage = xdebug_get_code_coverage(); + xdebug_stop_code_coverage(); + + return $codeCoverage; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Exception.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Exception.php new file mode 100644 index 0000000..18cd14d --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Exception.php @@ -0,0 +1,59 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Eyception class for PHP_CodeCoverage component. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Exception extends RuntimeException +{ +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Filter.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Filter.php new file mode 100644 index 0000000..d98f6a8 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Filter.php @@ -0,0 +1,343 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Filter for blacklisting and whitelisting of code coverage information. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_Filter +{ + /** + * Source files that are blacklisted. + * + * @var array + */ + protected $blacklistedFiles = array(); + + /** + * Source files that are whitelisted. + * + * @var array + */ + protected $whitelistedFiles = array(); + + /** + * @var boolean + */ + protected $blacklistPrefilled = FALSE; + + /** + * Adds a directory to the blacklist (recursively). + * + * @param string $directory + * @param string $suffix + * @param string $prefix + */ + public function addDirectoryToBlacklist($directory, $suffix = '.php', $prefix = '') + { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $directory, $suffix, $prefix + ); + + foreach ($files as $file) { + $this->addFileToBlacklist($file); + } + } + + /** + * Adds a file to the blacklist. + * + * @param string $filename + */ + public function addFileToBlacklist($filename) + { + $this->blacklistedFiles[realpath($filename)] = TRUE; + } + + /** + * Adds files to the blacklist. + * + * @param array $files + */ + public function addFilesToBlacklist(array $files) + { + foreach ($files as $file) { + $this->addFileToBlacklist($file); + } + } + + /** + * Removes a directory from the blacklist (recursively). + * + * @param string $directory + * @param string $suffix + * @param string $prefix + */ + public function removeDirectoryFromBlacklist($directory, $suffix = '.php', $prefix = '') + { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $directory, $suffix, $prefix + ); + + foreach ($files as $file) { + $this->removeFileFromBlacklist($file); + } + } + + /** + * Removes a file from the blacklist. + * + * @param string $filename + */ + public function removeFileFromBlacklist($filename) + { + $filename = realpath($filename); + + if (isset($this->blacklistedFiles[$filename])) { + unset($this->blacklistedFiles[$filename]); + } + } + + /** + * Adds a directory to the whitelist (recursively). + * + * @param string $directory + * @param string $suffix + * @param string $prefix + */ + public function addDirectoryToWhitelist($directory, $suffix = '.php', $prefix = '') + { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $directory, $suffix, $prefix + ); + + foreach ($files as $file) { + $this->addFileToWhitelist($file); + } + } + + /** + * Adds a file to the whitelist. + * + * @param string $filename + */ + public function addFileToWhitelist($filename) + { + $this->whitelistedFiles[realpath($filename)] = TRUE; + } + + /** + * Adds files to the whitelist. + * + * @param array $files + */ + public function addFilesToWhitelist(array $files) + { + foreach ($files as $file) { + $this->addFileToWhitelist($file); + } + } + + /** + * Removes a directory from the whitelist (recursively). + * + * @param string $directory + * @param string $suffix + * @param string $prefix + */ + public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '') + { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $directory, $suffix, $prefix + ); + + foreach ($files as $file) { + $this->removeFileFromWhitelist($file); + } + } + + /** + * Removes a file from the whitelist. + * + * @param string $filename + */ + public function removeFileFromWhitelist($filename) + { + $filename = realpath($filename); + + if (isset($this->whitelistedFiles[$filename])) { + unset($this->whitelistedFiles[$filename]); + } + } + + /** + * Checks whether a filename is a real filename. + * + * @param string $filename + */ + public function isFile($filename) + { + if ($filename == '-' || + strpos($filename, 'eval()\'d code') !== FALSE || + strpos($filename, 'runtime-created function') !== FALSE || + strpos($filename, 'runkit created function') !== FALSE || + strpos($filename, 'assert code') !== FALSE || + strpos($filename, 'regexp code') !== FALSE) { + return FALSE; + } + + return TRUE; + } + + /** + * Checks whether or not a file is filtered. + * + * When the whitelist is empty (default), blacklisting is used. + * When the whitelist is not empty, whitelisting is used. + * + * @param string $filename + * @param boolean $ignoreWhitelist + * @return boolean + * @throws PHP_CodeCoverage_Exception + */ + public function isFiltered($filename) + { + $filename = realpath($filename); + + if (!empty($this->whitelistedFiles)) { + return !isset($this->whitelistedFiles[$filename]); + } + + if (!$this->blacklistPrefilled) { + $this->prefillBlacklist(); + } + + return isset($this->blacklistedFiles[$filename]); + } + + /** + * Returns the list of blacklisted files. + * + * @return array + */ + public function getBlacklist() + { + return array_keys($this->blacklistedFiles); + } + + /** + * Returns the list of whitelisted files. + * + * @return array + */ + public function getWhitelist() + { + return array_keys($this->whitelistedFiles); + } + + /** + * Returns whether this filter has a whitelist. + * + * @return boolean + * @since Method available since Release 1.1.0 + */ + public function hasWhitelist() + { + return !empty($this->whitelistedFiles); + } + + /** + * @since Method available since Release 1.2.3 + */ + protected function prefillBlacklist() + { + $this->addDirectoryContainingClassToBlacklist('File_Iterator'); + $this->addDirectoryContainingClassToBlacklist('PHP_CodeCoverage'); + $this->addDirectoryContainingClassToBlacklist('PHP_Invoker'); + $this->addDirectoryContainingClassToBlacklist('PHP_Timer'); + $this->addDirectoryContainingClassToBlacklist('PHP_Token'); + $this->addDirectoryContainingClassToBlacklist('PHPUnit_Framework_TestCase', 2); + $this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_Database_TestCase', 2); + $this->addDirectoryContainingClassToBlacklist('PHPUnit_Framework_MockObject_Generator', 2); + $this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_SeleniumTestCase', 2); + $this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_Story_TestCase', 2); + $this->addDirectoryContainingClassToBlacklist('Text_Template'); + $this->addDirectoryContainingClassToBlacklist('Symfony\Component\Yaml\Yaml'); + + $this->blacklistPrefilled = TRUE; + } + + /** + * @param string $className + * @param integer $parent + * @since Method available since Release 1.2.3 + */ + protected function addDirectoryContainingClassToBlacklist($className, $parent = 1) + { + if (!class_exists($className)) { + return; + } + + $reflector = new ReflectionClass($className); + $directory = $reflector->getFileName(); + + for ($i = 0; $i < $parent; $i++) { + $directory = dirname($directory); + } + + $this->addDirectoryToBlacklist($directory); + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Clover.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Clover.php new file mode 100644 index 0000000..050c622 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Clover.php @@ -0,0 +1,346 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Generates a Clover XML logfile from an PHP_CodeCoverage object. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_Report_Clover +{ + /** + * @param PHP_CodeCoverage $coverage + * @param string $target + * @param string $name + * @return string + */ + public function process(PHP_CodeCoverage $coverage, $target = NULL, $name = NULL) + { + $xmlDocument = new DOMDocument('1.0', 'UTF-8'); + $xmlDocument->formatOutput = TRUE; + + $xmlCoverage = $xmlDocument->createElement('coverage'); + $xmlCoverage->setAttribute('generated', (int)$_SERVER['REQUEST_TIME']); + $xmlDocument->appendChild($xmlCoverage); + + $xmlProject = $xmlDocument->createElement('project'); + $xmlProject->setAttribute('timestamp', (int)$_SERVER['REQUEST_TIME']); + + if (is_string($name)) { + $xmlProject->setAttribute('name', $name); + } + + $xmlCoverage->appendChild($xmlProject); + + $packages = array(); + $report = $coverage->getReport(); + unset($coverage); + + foreach ($report as $item) { + $namespace = 'global'; + + if (!$item instanceof PHP_CodeCoverage_Report_Node_File) { + continue; + } + + $xmlFile = $xmlDocument->createElement('file'); + $xmlFile->setAttribute('name', $item->getPath()); + + $classes = $item->getClassesAndTraits(); + $coverage = $item->getCoverageData(); + $lines = array(); + $ignoredLines = $item->getIgnoredLines(); + + foreach ($classes as $className => $class) { + $classStatements = 0; + $coveredClassStatements = 0; + $coveredMethods = 0; + + foreach ($class['methods'] as $methodName => $method) { + $methodCount = 0; + $methodLines = 0; + $methodLinesCovered = 0; + + for ($i = $method['startLine']; + $i <= $method['endLine']; + $i++) { + if (isset($ignoredLines[$i])) { + continue; + } + + $add = TRUE; + $count = 0; + + if (isset($coverage[$i])) { + if ($coverage[$i] !== NULL) { + $classStatements++; + $methodLines++; + } else { + $add = FALSE; + } + + $count = count($coverage[$i]); + + if ($count > 0) { + $coveredClassStatements++; + $methodLinesCovered++; + } + } else { + $add = FALSE; + } + + $methodCount = max($methodCount, $count); + + if ($add) { + $lines[$i] = array( + 'count' => $count, 'type' => 'stmt' + ); + } + } + + if ($methodCount > 0) { + $coveredMethods++; + } + + $lines[$method['startLine']] = array( + 'count' => $methodCount, + 'crap' => $method['crap'], + 'type' => 'method', + 'name' => $methodName + ); + } + + if (!empty($class['package']['namespace'])) { + $namespace = $class['package']['namespace']; + } + + $xmlClass = $xmlDocument->createElement('class'); + $xmlClass->setAttribute('name', $className); + $xmlClass->setAttribute('namespace', $namespace); + + if (!empty($class['package']['fullPackage'])) { + $xmlClass->setAttribute( + 'fullPackage', $class['package']['fullPackage'] + ); + } + + if (!empty($class['package']['category'])) { + $xmlClass->setAttribute( + 'category', $class['package']['category'] + ); + } + + if (!empty($class['package']['package'])) { + $xmlClass->setAttribute( + 'package', $class['package']['package'] + ); + } + + if (!empty($class['package']['subpackage'])) { + $xmlClass->setAttribute( + 'subpackage', $class['package']['subpackage'] + ); + } + + $xmlFile->appendChild($xmlClass); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('methods', count($class['methods'])); + $xmlMetrics->setAttribute('coveredmethods', $coveredMethods); + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute('statements', $classStatements); + $xmlMetrics->setAttribute( + 'coveredstatements', $coveredClassStatements + ); + $xmlMetrics->setAttribute( + 'elements', + count($class['methods']) + + $classStatements + /* + conditionals */); + $xmlMetrics->setAttribute( + 'coveredelements', + $coveredMethods + + $coveredClassStatements + /* + coveredconditionals */ + ); + $xmlClass->appendChild($xmlMetrics); + } + + foreach ($coverage as $line => $data) { + if ($data === NULL || + isset($lines[$line]) || + isset($ignoredLines[$line])) { + continue; + } + + $lines[$line] = array( + 'count' => count($data), 'type' => 'stmt' + ); + } + + ksort($lines); + + foreach ($lines as $line => $data) { + if (isset($ignoredLines[$line])) { + continue; + } + + $xmlLine = $xmlDocument->createElement('line'); + $xmlLine->setAttribute('num', $line); + $xmlLine->setAttribute('type', $data['type']); + + if (isset($data['name'])) { + $xmlLine->setAttribute('name', $data['name']); + } + + if (isset($data['crap'])) { + $xmlLine->setAttribute('crap', $data['crap']); + } + + $xmlLine->setAttribute('count', $data['count']); + $xmlFile->appendChild($xmlLine); + } + + $linesOfCode = $item->getLinesOfCode(); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); + $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); + $xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits()); + $xmlMetrics->setAttribute('methods', $item->getNumMethods()); + $xmlMetrics->setAttribute( + 'coveredmethods', $item->getNumTestedMethods() + ); + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute( + 'statements', $item->getNumExecutableLines() + ); + $xmlMetrics->setAttribute( + 'coveredstatements', $item->getNumExecutedLines() + ); + $xmlMetrics->setAttribute( + 'elements', + $item->getNumMethods() + + $item->getNumExecutableLines() + /* + conditionals */ + ); + $xmlMetrics->setAttribute( + 'coveredelements', + $item->getNumTestedMethods() + + $item->getNumExecutedLines() + /* + coveredconditionals */ + ); + $xmlFile->appendChild($xmlMetrics); + + if ($namespace == 'global') { + $xmlProject->appendChild($xmlFile); + } else { + if (!isset($packages[$namespace])) { + $packages[$namespace] = $xmlDocument->createElement( + 'package' + ); + + $packages[$namespace]->setAttribute('name', $namespace); + $xmlProject->appendChild($packages[$namespace]); + } + + $packages[$namespace]->appendChild($xmlFile); + } + } + + $linesOfCode = $report->getLinesOfCode(); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('files', count($report)); + $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); + $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); + $xmlMetrics->setAttribute( + 'classes', $report->getNumClassesAndTraits() + ); + $xmlMetrics->setAttribute('methods', $report->getNumMethods()); + $xmlMetrics->setAttribute( + 'coveredmethods', $report->getNumTestedMethods() + ); + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute( + 'statements', $report->getNumExecutableLines() + ); + $xmlMetrics->setAttribute( + 'coveredstatements', $report->getNumExecutedLines() + ); + $xmlMetrics->setAttribute( + 'elements', + $report->getNumMethods() + + $report->getNumExecutableLines() + /* + conditionals */ + ); + $xmlMetrics->setAttribute( + 'coveredelements', + $report->getNumTestedMethods() + + $report->getNumExecutedLines() + /* + coveredconditionals */ + ); + $xmlProject->appendChild($xmlMetrics); + + if ($target !== NULL) { + if (!is_dir(dirname($target))) { + mkdir(dirname($target), 0777, TRUE); + } + + return $xmlDocument->save($target); + } else { + return $xmlDocument->saveXML(); + } + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Factory.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Factory.php new file mode 100644 index 0000000..e69c457 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Factory.php @@ -0,0 +1,280 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Factory for PHP_CodeCoverage_Report_Node_* object graphs. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_Factory +{ + /** + * @param PHP_CodeCoverage $coverage + */ + public function create(PHP_CodeCoverage $coverage) + { + $files = $coverage->getData(); + $commonPath = $this->reducePaths($files); + $root = new PHP_CodeCoverage_Report_Node_Directory( + $commonPath, NULL + ); + + $this->addItems( + $root, + $this->buildDirectoryStructure($files), + $coverage->getTests(), + $coverage->getCacheTokens() + ); + + return $root; + } + + /** + * @param PHP_CodeCoverage_Report_Node_Directory $root + * @param array $items + * @param array $tests + * @param boolean $cacheTokens + */ + protected function addItems(PHP_CodeCoverage_Report_Node_Directory $root, array $items, array $tests, $cacheTokens) + { + foreach ($items as $key => $value) { + if (substr($key, -2) == '/f') { + $key = substr($key, 0, -2); + + if (file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) { + $root->addFile($key, $value, $tests, $cacheTokens); + } + } else { + $child = $root->addDirectory($key); + $this->addItems($child, $value, $tests, $cacheTokens); + } + } + } + + /** + * Builds an array representation of the directory structure. + * + * For instance, + * + * + * Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * is transformed into + * + * + * Array + * ( + * [.] => Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * ) + * + * + * @param array $files + * @return array + */ + protected function buildDirectoryStructure($files) + { + $result = array(); + + foreach ($files as $path => $file) { + $path = explode('/', $path); + $pointer = &$result; + $max = count($path); + + for ($i = 0; $i < $max; $i++) { + if ($i == ($max - 1)) { + $type = '/f'; + } else { + $type = ''; + } + + $pointer = &$pointer[$path[$i] . $type]; + } + + $pointer = $file; + } + + return $result; + } + + /** + * Reduces the paths by cutting the longest common start path. + * + * For instance, + * + * + * Array + * ( + * [/home/sb/Money/Money.php] => Array + * ( + * ... + * ) + * + * [/home/sb/Money/MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * is reduced to + * + * + * Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * @param array $files + * @return string + */ + protected function reducePaths(&$files) + { + if (empty($files)) { + return '.'; + } + + $commonPath = ''; + $paths = array_keys($files); + + if (count($files) == 1) { + $commonPath = dirname($paths[0]) . '/'; + $files[basename($paths[0])] = $files[$paths[0]]; + + unset($files[$paths[0]]); + + return $commonPath; + } + + $max = count($paths); + + for ($i = 0; $i < $max; $i++) { + // strip phar:// prefixes + if (strpos($paths[$i], 'phar://') === 0) { + $paths[$i] = substr($paths[$i], 7); + } + $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]); + + if (empty($paths[$i][0])) { + $paths[$i][0] = DIRECTORY_SEPARATOR; + } + } + + $done = FALSE; + $max = count($paths); + + while (!$done) { + for ($i = 0; $i < $max - 1; $i++) { + if (!isset($paths[$i][0]) || + !isset($paths[$i+1][0]) || + $paths[$i][0] != $paths[$i+1][0]) { + $done = TRUE; + break; + } + } + + if (!$done) { + $commonPath .= $paths[0][0]; + + if ($paths[0][0] != DIRECTORY_SEPARATOR) { + $commonPath .= DIRECTORY_SEPARATOR; + } + + for ($i = 0; $i < $max; $i++) { + array_shift($paths[$i]); + } + } + } + + $original = array_keys($files); + $max = count($original); + + for ($i = 0; $i < $max; $i++) { + $files[join('/', $paths[$i])] = $files[$original[$i]]; + unset($files[$original[$i]]); + } + + ksort($files); + + return substr($commonPath, 0, -1); + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML.php new file mode 100644 index 0000000..5db8731 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML.php @@ -0,0 +1,221 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Generates an HTML report from an PHP_CodeCoverage object. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_Report_HTML +{ + /** + * @var string + */ + protected $templatePath; + + /** + * @var string + */ + protected $charset; + + /** + * @var string + */ + protected $generator; + + /** + * @var integer + */ + protected $lowUpperBound; + + /** + * @var integer + */ + protected $highLowerBound; + + /** + * @var boolean + */ + protected $highlight; + + /** + * Constructor. + * + * @param array $options + */ + public function __construct($charset = 'UTF-8', $highlight = FALSE, $lowUpperBound = 35, $highLowerBound = 70, $generator = '') + { + $this->charset = $charset; + $this->generator = $generator; + $this->highLowerBound = $highLowerBound; + $this->highlight = $highlight; + $this->lowUpperBound = $lowUpperBound; + + $this->templatePath = sprintf( + '%s%sHTML%sRenderer%sTemplate%s', + + dirname(__FILE__), + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR + ); + } + + /** + * @param PHP_CodeCoverage $coverage + * @param string $target + */ + public function process(PHP_CodeCoverage $coverage, $target) + { + $target = $this->getDirectory($target); + $report = $coverage->getReport(); + unset($coverage); + + if (!isset($_SERVER['REQUEST_TIME'])) { + $_SERVER['REQUEST_TIME'] = time(); + } + + $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); + + $dashboard = new PHP_CodeCoverage_Report_HTML_Renderer_Dashboard( + $this->templatePath, + $this->charset, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound + ); + + $directory = new PHP_CodeCoverage_Report_HTML_Renderer_Directory( + $this->templatePath, + $this->charset, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound + ); + + $file = new PHP_CodeCoverage_Report_HTML_Renderer_File( + $this->templatePath, + $this->charset, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound, + $this->highlight + ); + + $dashboard->render($report, $target . 'index.dashboard.html'); + $directory->render($report, $target . 'index.html'); + + foreach ($report as $node) { + $id = $node->getId(); + + if ($node instanceof PHP_CodeCoverage_Report_Node_Directory) { + $dashboard->render($node, $target . $id . '.dashboard.html'); + $directory->render($node, $target . $id . '.html'); + } else { + $file->render($node, $target . $id . '.html'); + } + } + + $this->copyFiles($target); + } + + /** + * @param string $target + */ + protected function copyFiles($target) + { + $dir = $this->getDirectory($target . 'css'); + copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); + copy($this->templatePath . 'css/bootstrap-responsive.min.css', $dir . 'bootstrap-responsive.min.css'); + copy($this->templatePath . 'css/style.css', $dir . 'style.css'); + + $dir = $this->getDirectory($target . 'js'); + copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); + copy($this->templatePath . 'js/highcharts.js', $dir . 'highcharts.js'); + copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); + + $dir = $this->getDirectory($target . 'img'); + copy($this->templatePath . 'img/glyphicons-halflings.png', $dir . 'glyphicons-halflings.png'); + copy($this->templatePath . 'img/glyphicons-halflings-white.png', $dir . 'glyphicons-halflings-white.png'); + } + + /** + * @param string $directory + * @return string + * @throws PHP_CodeCoverage_Exception + * @since Method available since Release 1.2.0 + */ + protected function getDirectory($directory) + { + if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { + $directory .= DIRECTORY_SEPARATOR; + } + + if (is_dir($directory)) { + return $directory; + } + + if (mkdir($directory, 0777, TRUE)) { + return $directory; + } + + throw new PHP_CodeCoverage_Exception( + sprintf( + 'Directory "%s" does not exist.', + $directory + ) + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer.php new file mode 100644 index 0000000..b0c84bc --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer.php @@ -0,0 +1,269 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Base class for PHP_CodeCoverage_Report_Node renderers. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +abstract class PHP_CodeCoverage_Report_HTML_Renderer +{ + /** + * @var string + */ + protected $templatePath; + + /** + * @var string + */ + protected $charset; + + /** + * @var string + */ + protected $generator; + + /** + * @var string + */ + protected $date; + + /** + * @var integer + */ + protected $lowUpperBound; + + /** + * @var integer + */ + protected $highLowerBound; + + /** + * Constructor. + * + * @param string $templatePath + * @param string $charset + * @param string $generator + * @param string $date + * @param integer $lowUpperBound + * @param integer $highLowerBound + */ + public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound) + { + $this->templatePath = $templatePath; + $this->charset = $charset; + $this->generator = $generator; + $this->date = $date; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + } + + /** + * @param Text_Template $template + * @param array $data + * @return string + */ + protected function renderItemTemplate(Text_Template $template, array $data) + { + $classesBar = ' '; + $classesLevel = 'None'; + $classesNumber = ' '; + + if (isset($data['numClasses']) && $data['numClasses'] > 0) { + $classesLevel = $this->getColorLevel($data['testedClassesPercent']); + + $classesNumber = $data['numTestedClasses'] . ' / ' . + $data['numClasses']; + + $classesBar = $this->getCoverageBar( + $data['testedClassesPercent'] + ); + } + + $methodsBar = ' '; + $methodsLevel = 'None'; + $methodsNumber = ' '; + + if ($data['numMethods'] > 0) { + $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); + + $methodsNumber = $data['numTestedMethods'] . ' / ' . + $data['numMethods']; + + $methodsBar = $this->getCoverageBar( + $data['testedMethodsPercent'] + ); + } + + $linesBar = ' '; + $linesLevel = 'None'; + $linesNumber = ' '; + + if ($data['numExecutableLines'] > 0) { + $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); + + $linesNumber = $data['numExecutedLines'] . ' / ' . + $data['numExecutableLines']; + + $linesBar = $this->getCoverageBar( + $data['linesExecutedPercent'] + ); + } + + $template->setVar( + array( + 'icon' => isset($data['icon']) ? $data['icon'] : '', + 'crap' => isset($data['crap']) ? $data['crap'] : '', + 'name' => $data['name'], + 'lines_bar' => $linesBar, + 'lines_executed_percent' => $data['linesExecutedPercentAsString'], + 'lines_level' => $linesLevel, + 'lines_number' => $linesNumber, + 'methods_bar' => $methodsBar, + 'methods_tested_percent' => $data['testedMethodsPercentAsString'], + 'methods_level' => $methodsLevel, + 'methods_number' => $methodsNumber, + 'classes_bar' => $classesBar, + 'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '', + 'classes_level' => $classesLevel, + 'classes_number' => $classesNumber + ) + ); + + return $template->render(); + } + + /** + * @param Text_Template $template + * @param PHP_CodeCoverage_Report_Node $node + */ + protected function setCommonTemplateVariables(Text_Template $template, PHP_CodeCoverage_Report_Node $node) + { + $template->setVar( + array( + 'id' => $node->getId(), + 'full_path' => $node->getPath(), + 'breadcrumbs' => $this->getBreadcrumbs($node), + 'charset' => $this->charset, + 'date' => $this->date, + 'version' => PHP_CodeCoverage_Version::id(), + 'php_version' => PHP_VERSION, + 'generator' => $this->generator, + 'low_upper_bound' => $this->lowUpperBound, + 'high_lower_bound' => $this->highLowerBound + ) + ); + } + + protected function getBreadcrumbs(PHP_CodeCoverage_Report_Node $node) + { + $breadcrumbs = ''; + + $path = $node->getPathAsArray(); + + foreach ($path as $step) { + if ($step !== $node) { + $breadcrumbs .= sprintf( + '
    • %s /
    • ' . "\n", + $step->getId(), + $step->getName() + ); + } else { + $breadcrumbs .= sprintf( + '
    • %s
    • ' . "\n", + $step->getName() + ); + + if ($node instanceof PHP_CodeCoverage_Report_Node_Directory) { + $breadcrumbs .= sprintf( + '
    • (Dashboard)
    • ' . "\n", + $step->getId() + ); + } + } + } + + return $breadcrumbs; + } + + protected function getCoverageBar($percent) + { + $level = $this->getColorLevel($percent); + + $template = new Text_Template( + $this->templatePath . 'coverage_bar.html' + ); + + $template->setVar(array('level' => $level, 'percent' => sprintf("%.2F", $percent))); + + return $template->render(); + } + + /** + * @param integer $percent + * @return string + */ + protected function getColorLevel($percent) + { + if ($percent < $this->lowUpperBound) { + return 'danger'; + } + + else if ($percent >= $this->lowUpperBound && + $percent < $this->highLowerBound) { + return 'warning'; + } + + else { + return 'success'; + } + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php new file mode 100644 index 0000000..7d7446c --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php @@ -0,0 +1,256 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Renders the dashboard for a PHP_CodeCoverage_Report_Node_Directory node. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_HTML_Renderer_Dashboard extends PHP_CodeCoverage_Report_HTML_Renderer +{ + /** + * @param PHP_CodeCoverage_Report_Node_Directory $node + * @param string $file + */ + public function render(PHP_CodeCoverage_Report_Node_Directory $node, $file) + { + $classes = $node->getClassesAndTraits(); + $template = new Text_Template( + $this->templatePath . 'dashboard.html' + ); + + $this->setCommonTemplateVariables($template, $node); + + $template->setVar( + array( + 'least_tested_methods' => $this->leastTestedMethods($classes), + 'top_project_risks' => $this->topProjectRisks($classes), + 'cc_values' => $this->classComplexity($classes), + 'ccd_values' => $this->classCoverageDistribution($classes), + 'backlink' => basename(str_replace('.dashboard', '', $file)) + ) + ); + + $template->renderTo($file); + } + + /** + * Returns the data for the Class Complexity chart. + * + * @param array $classes + * @return string + */ + protected function classComplexity(array $classes) + { + $data = array(); + + foreach ($classes as $name => $class) { + $data[] = array( + $class['coverage'], + $class['ccn'], + sprintf( + '%s', + $class['link'], + $name + ) + ); + } + + return json_encode($data); + } + + /** + * Returns the data for the Class Coverage Distribution chart. + * + * @param array $classes + * @return string + */ + protected function classCoverageDistribution(array $classes) + { + $data = array( + '0%' => 0, + '0-10%' => 0, + '10-20%' => 0, + '20-30%' => 0, + '30-40%' => 0, + '40-50%' => 0, + '50-60%' => 0, + '60-70%' => 0, + '70-80%' => 0, + '80-90%' => 0, + '90-100%' => 0, + '100%' => 0 + ); + + foreach ($classes as $class) { + if ($class['coverage'] == 0) { + $data['0%']++; + } + + else if ($class['coverage'] == 100) { + $data['100%']++; + } + + else { + $key = floor($class['coverage']/10)*10; + $key = $key . '-' . ($key + 10) . '%'; + $data[$key]++; + } + } + + return json_encode(array_values($data)); + } + + /** + * Returns the least tested methods. + * + * @param array $classes + * @param integer $max + * @return string + */ + protected function leastTestedMethods(array $classes, $max = 10) + { + $methods = array(); + + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] < 100) { + if ($className != '*') { + $key = $className . '::' . $methodName; + } else { + $key = $methodName; + } + + $methods[$key] = $method['coverage']; + } + } + } + + asort($methods); + + $methods = array_slice($methods, 0, min($max, count($methods))); + $buffer = ''; + + foreach ($methods as $name => $coverage) { + list($class, $method) = explode('::', $name); + + $buffer .= sprintf( + '
    • %s (%d%%)
    • ' . "\n", + $classes[$class]['methods'][$method]['link'], + $name, + $coverage + ); + } + + return $buffer; + } + + /** + * Returns the top project risks according to the CRAP index. + * + * @param array $classes + * @param integer $max + * @return string + */ + protected function topProjectRisks(array $classes, $max = 10) + { + $risks = array(); + + foreach ($classes as $className => $class) { + if ($class['coverage'] < 100 && + $class['ccn'] > count($class['methods'])) { + $risks[$className] = $class['crap']; + } + } + + arsort($risks); + + $buffer = ''; + $risks = array_slice($risks, 0, min($max, count($risks))); + + foreach ($risks as $name => $crap) { + $buffer .= sprintf( + '
    • %s (%d)
    • ' . "\n", + $classes[$name]['link'], + $name, + $crap + ); + } + + return $buffer; + } + + protected function getBreadcrumbs(PHP_CodeCoverage_Report_Node $node) + { + $breadcrumbs = ''; + + $path = $node->getPathAsArray(); + + foreach ($path as $step) { + if ($step !== $node) { + $breadcrumbs .= sprintf( + '
    • %s /
    • ' . "\n", + $step->getId(), + $step->getName() + ); + } else { + $breadcrumbs .= sprintf( + '
    • %s
    • ' . "\n" . + '
    • (Dashboard)
    • ' . "\n", + $step->getId(), + $step->getName() + ); + } + } + + return $breadcrumbs; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php new file mode 100644 index 0000000..ed05276 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php @@ -0,0 +1,132 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Renders a PHP_CodeCoverage_Report_Node_Directory node. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_HTML_Renderer_Directory extends PHP_CodeCoverage_Report_HTML_Renderer +{ + /** + * @param PHP_CodeCoverage_Report_Node_Directory $node + * @param string $file + */ + public function render(PHP_CodeCoverage_Report_Node_Directory $node, $file) + { + $template = new Text_Template($this->templatePath . 'directory.html'); + + $this->setCommonTemplateVariables($template, $node); + + $items = $this->renderItem($node, TRUE); + + foreach ($node->getDirectories() as $item) { + $items .= $this->renderItem($item); + } + + foreach ($node->getFiles() as $item) { + $items .= $this->renderItem($item); + } + + $template->setVar( + array( + 'id' => $node->getId(), + 'items' => $items + ) + ); + + $template->renderTo($file); + } + + /** + * @param PHP_CodeCoverage_Report_Node $item + * @param boolean $total + * @return string + */ + protected function renderItem(PHP_CodeCoverage_Report_Node $item, $total = FALSE) + { + $data = array( + 'numClasses' => $item->getNumClassesAndTraits(), + 'numTestedClasses' => $item->getNumTestedClassesAndTraits(), + 'numMethods' => $item->getNumMethods(), + 'numTestedMethods' => $item->getNumTestedMethods(), + 'linesExecutedPercent' => $item->getLineExecutedPercent(FALSE), + 'linesExecutedPercentAsString' => $item->getLineExecutedPercent(), + 'numExecutedLines' => $item->getNumExecutedLines(), + 'numExecutableLines' => $item->getNumExecutableLines(), + 'testedMethodsPercent' => $item->getTestedMethodsPercent(FALSE), + 'testedMethodsPercentAsString' => $item->getTestedMethodsPercent(), + 'testedClassesPercent' => $item->getTestedClassesAndTraitsPercent(FALSE), + 'testedClassesPercentAsString' => $item->getTestedClassesAndTraitsPercent() + ); + + if ($total) { + $data['name'] = 'Total'; + } else { + $data['name'] = sprintf( + '%s', + $item->getId(), + $item->getName() + ); + + if ($item instanceof PHP_CodeCoverage_Report_Node_Directory) { + $data['icon'] = ' '; + } else { + $data['icon'] = ' '; + } + } + + return $this->renderItemTemplate( + new Text_Template($this->templatePath . 'directory_item.html'), + $data + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/File.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/File.php new file mode 100644 index 0000000..9150d77 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/File.php @@ -0,0 +1,583 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +// @codeCoverageIgnoreStart +if (!defined('T_TRAIT')) { + define('T_TRAIT', 1001); +} + +if (!defined('T_INSTEADOF')) { + define('T_INSTEADOF', 1002); +} + +if (!defined('T_CALLABLE')) { + define('T_CALLABLE', 1003); +} +// @codeCoverageIgnoreEnd + +/** + * Renders a PHP_CodeCoverage_Report_Node_File node. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_HTML_Renderer_File extends PHP_CodeCoverage_Report_HTML_Renderer +{ + /** + * @var boolean + */ + protected $highlight; + + /** + * Constructor. + * + * @param string $templatePath + * @param string $charset + * @param string $generator + * @param string $date + * @param integer $lowUpperBound + * @param integer $highLowerBound + * @param boolean $highlight + */ + public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound, $highlight) + { + parent::__construct( + $templatePath, + $charset, + $generator, + $date, + $lowUpperBound, + $highLowerBound + ); + + $this->highlight = $highlight; + } + + /** + * @param PHP_CodeCoverage_Report_Node_File $node + * @param string $file + */ + public function render(PHP_CodeCoverage_Report_Node_File $node, $file) + { + $template = new Text_Template($this->templatePath . 'file.html'); + + $template->setVar( + array( + 'items' => $this->renderItems($node), + 'lines' => $this->renderSource($node) + ) + ); + + $this->setCommonTemplateVariables($template, $node); + + $template->renderTo($file); + } + + /** + * @param PHP_CodeCoverage_Report_Node_File $node + * @return string + */ + protected function renderItems(PHP_CodeCoverage_Report_Node_File $node) + { + $template = new Text_Template($this->templatePath . 'file_item.html'); + + $methodItemTemplate = new Text_Template( + $this->templatePath . 'method_item.html' + ); + + $items = $this->renderItemTemplate( + $template, + array( + 'name' => 'Total', + 'numClasses' => $node->getNumClassesAndTraits(), + 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), + 'numMethods' => $node->getNumMethods(), + 'numTestedMethods' => $node->getNumTestedMethods(), + 'linesExecutedPercent' => $node->getLineExecutedPercent(FALSE), + 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), + 'numExecutedLines' => $node->getNumExecutedLines(), + 'numExecutableLines' => $node->getNumExecutableLines(), + 'testedMethodsPercent' => $node->getTestedMethodsPercent(FALSE), + 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(), + 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(FALSE), + 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), + 'crap' => 'CRAP' + ) + ); + + $items .= $this->renderFunctionItems( + $node->getFunctions(), $methodItemTemplate + ); + + $items .= $this->renderTraitOrClassItems( + $node->getTraits(), $template, $methodItemTemplate + ); + + $items .= $this->renderTraitOrClassItems( + $node->getClasses(), $template, $methodItemTemplate + ); + + return $items; + } + + /** + * @param array $items + * @param Text_Template $template + * @return string + */ + protected function renderTraitOrClassItems(array $items, Text_Template $template, Text_Template $methodItemTemplate) + { + if (empty($items)) { + return ''; + } + + $buffer = ''; + + foreach ($items as $name => $item) { + $numMethods = count($item['methods']); + $numTestedMethods = 0; + + foreach ($item['methods'] as $method) { + if ($method['executedLines'] == $method['executableLines']) { + $numTestedMethods++; + } + } + + $buffer .= $this->renderItemTemplate( + $template, + array( + 'name' => $name, + 'numClasses' => 1, + 'numTestedClasses' => $numTestedMethods == $numMethods ? 1 : 0, + 'numMethods' => $numMethods, + 'numTestedMethods' => $numTestedMethods, + 'linesExecutedPercent' => PHP_CodeCoverage_Util::percent( + $item['executedLines'], + $item['executableLines'], + FALSE + ), + 'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent( + $item['executedLines'], + $item['executableLines'], + TRUE + ), + 'numExecutedLines' => $item['executedLines'], + 'numExecutableLines' => $item['executableLines'], + 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent( + $numTestedMethods, + $numMethods, + FALSE + ), + 'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent( + $numTestedMethods, + $numMethods, + TRUE + ), + 'testedClassesPercent' => PHP_CodeCoverage_Util::percent( + $numTestedMethods == $numMethods ? 1 : 0, + 1, + FALSE + ), + 'testedClassesPercentAsString' => PHP_CodeCoverage_Util::percent( + $numTestedMethods == $numMethods ? 1 : 0, + 1, + TRUE + ), + 'crap' => $item['crap'] + ) + ); + + foreach ($item['methods'] as $method) { + $buffer .= $this->renderFunctionOrMethodItem( + $methodItemTemplate, $method, ' ' + ); + } + } + + return $buffer; + } + + /** + * @param array $functions + * @param Text_Template $template + * @return string + */ + protected function renderFunctionItems(array $functions, Text_Template $template) + { + if (empty($functions)) { + return ''; + } + + $buffer = ''; + + foreach ($functions as $function) { + $buffer .= $this->renderFunctionOrMethodItem( + $template, $function + ); + } + + return $buffer; + } + + /** + * @param Text_Template $template + * @return string + */ + protected function renderFunctionOrMethodItem(Text_Template $template, array $item, $indent = '') + { + $numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0; + + return $this->renderItemTemplate( + $template, + array( + 'name' => sprintf( + '%s%s', + $indent, + $item['startLine'], + htmlspecialchars($item['signature']) + ), + 'numMethods' => 1, + 'numTestedMethods' => $numTestedItems, + 'linesExecutedPercent' => PHP_CodeCoverage_Util::percent( + $item['executedLines'], + $item['executableLines'], + FALSE + ), + 'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent( + $item['executedLines'], + $item['executableLines'], + TRUE + ), + 'numExecutedLines' => $item['executedLines'], + 'numExecutableLines' => $item['executableLines'], + 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent( + $numTestedItems, + 1, + FALSE + ), + 'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent( + $numTestedItems, + 1, + TRUE + ), + 'crap' => $item['crap'] + ) + ); + } + + /** + * @param PHP_CodeCoverage_Report_Node_File $node + * @return string + */ + protected function renderSource(PHP_CodeCoverage_Report_Node_File $node) + { + $coverageData = $node->getCoverageData(); + $ignoredLines = $node->getIgnoredLines(); + $testData = $node->getTestData(); + $codeLines = $this->loadFile($node->getPath()); + $lines = ''; + $i = 1; + + foreach ($codeLines as $line) { + $numTests = ''; + $trClass = ''; + $popoverContent = ''; + $popoverTitle = ''; + + if (!isset($ignoredLines[$i]) && isset($coverageData[$i])) { + $numTests = count($coverageData[$i]); + + if ($coverageData[$i] === NULL) { + $trClass = ' class="warning"'; + } + + else if ($numTests == 0) { + $trClass = ' class="danger"'; + } + + else { + $trClass = ' class="success popin"'; + $popoverContent = '
        '; + + if ($numTests > 1) { + $popoverTitle = $numTests . ' tests cover line ' . $i; + } else { + $popoverTitle = '1 test covers line ' . $i; + } + + foreach ($coverageData[$i] as $test) { + switch ($testData[$test]) { + case 0: { + $testCSS = ' class="success"'; + } + break; + + case 1: + case 2: { + $testCSS = ' class="warning"'; + } + break; + + case 3: { + $testCSS = ' class="danger"'; + } + break; + + case 4: { + $testCSS = ' class="danger"'; + } + break; + + default: { + $testCSS = ''; + } + } + + $popoverContent .= sprintf( + '%s', + + $testCSS, + htmlspecialchars($test) + ); + } + + $popoverContent .= '
      '; + } + } + + if (!empty($popoverTitle)) { + $popover = sprintf( + ' data-title="%s" data-content="%s" data-placement="bottom"', + $popoverTitle, + htmlspecialchars($popoverContent) + ); + } else { + $popover = ''; + } + + $lines .= sprintf( + ' %s' . "\n", + $trClass, + $popover, + $i, + $i, + $i, + !$this->highlight ? htmlspecialchars($line) : $line + ); + + $i++; + } + + return $lines; + } + + /** + * @param string $file + * @return array + */ + protected function loadFile($file) + { + $buffer = file_get_contents($file); + $lines = explode("\n", str_replace("\t", ' ', $buffer)); + $result = array(); + + if (count($lines) == 0) { + return $result; + } + + $lines = array_map('rtrim', $lines); + + if (!$this->highlight) { + unset($lines[count($lines)-1]); + return $lines; + } + + $tokens = token_get_all($buffer); + $stringFlag = FALSE; + $i = 0; + $result[$i] = ''; + + foreach ($tokens as $j => $token) { + if (is_string($token)) { + if ($token === '"' && $tokens[$j - 1] !== '\\') { + $result[$i] .= sprintf( + '%s', + + htmlspecialchars($token) + ); + + $stringFlag = !$stringFlag; + } else { + $result[$i] .= sprintf( + '%s', + + htmlspecialchars($token) + ); + } + + continue; + } + + list ($token, $value) = $token; + + $value = str_replace( + array("\t", ' '), + array('    ', ' '), + htmlspecialchars($value) + ); + + if ($value === "\n") { + $result[++$i] = ''; + } else { + $lines = explode("\n", $value); + + foreach ($lines as $jj => $line) { + $line = trim($line); + + if ($line !== '') { + if ($stringFlag) { + $colour = 'string'; + } else { + switch ($token) { + case T_INLINE_HTML: { + $colour = 'html'; + } + break; + + case T_COMMENT: + case T_DOC_COMMENT: { + $colour = 'comment'; + } + break; + + case T_ABSTRACT: + case T_ARRAY: + case T_AS: + case T_BREAK: + case T_CALLABLE: + case T_CASE: + case T_CATCH: + case T_CLASS: + case T_CLONE: + case T_CONTINUE: + case T_DEFAULT: + case T_ECHO: + case T_ELSE: + case T_ELSEIF: + case T_EMPTY: + case T_ENDDECLARE: + case T_ENDFOR: + case T_ENDFOREACH: + case T_ENDIF: + case T_ENDSWITCH: + case T_ENDWHILE: + case T_EXIT: + case T_EXTENDS: + case T_FINAL: + case T_FOREACH: + case T_FUNCTION: + case T_GLOBAL: + case T_IF: + case T_IMPLEMENTS: + case T_INCLUDE: + case T_INCLUDE_ONCE: + case T_INSTANCEOF: + case T_INSTEADOF: + case T_INTERFACE: + case T_ISSET: + case T_LOGICAL_AND: + case T_LOGICAL_OR: + case T_LOGICAL_XOR: + case T_NAMESPACE: + case T_NEW: + case T_PRIVATE: + case T_PROTECTED: + case T_PUBLIC: + case T_REQUIRE: + case T_REQUIRE_ONCE: + case T_RETURN: + case T_STATIC: + case T_THROW: + case T_TRAIT: + case T_TRY: + case T_UNSET: + case T_USE: + case T_VAR: + case T_WHILE: { + $colour = 'keyword'; + } + break; + + default: { + $colour = 'default'; + } + } + } + + $result[$i] .= sprintf( + '%s', + + $colour, + $line + ); + } + + if (isset($lines[$jj + 1])) { + $result[++$i] = ''; + } + } + } + } + + unset($result[count($result)-1]); + + return $result; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist new file mode 100644 index 0000000..73a11a1 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist @@ -0,0 +1,3 @@ +
      +
      +
      diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css new file mode 100644 index 0000000..7b0158d --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Responsive v2.1.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade.in{top:auto}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:block;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css new file mode 100644 index 0000000..31d8b96 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v2.1.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}.text-warning{color:#c09853}.text-error{color:#b94a48}.text-info{color:#3a87ad}.text-success{color:#468847}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:1;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1{font-size:36px;line-height:40px}h2{font-size:30px;line-height:40px}h3{font-size:24px;line-height:40px}h4{font-size:18px;line-height:20px}h5{font-size:14px;line-height:20px}h6{font-size:12px;line-height:20px}h1 small{font-size:24px}h2 small{font-size:18px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:9px;font-size:14px;line-height:20px;color:#555;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal;cursor:pointer}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:18px;padding-left:18px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"]{float:left}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;font-size:14px;vertical-align:top;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .add-on,.input-append .btn{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topleft:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table [class*=span],.row-fluid table [class*=span]{display:table-cell;float:none;margin-left:0}.table .span1{float:none;width:44px;margin-left:0}.table .span2{float:none;width:124px;margin-left:0}.table .span3{float:none;width:204px;margin-left:0}.table .span4{float:none;width:284px;margin-left:0}.table .span5{float:none;width:364px;margin-left:0}.table .span6{float:none;width:444px;margin-left:0}.table .span7{float:none;width:524px;margin-left:0}.table .span8{float:none;width:604px;margin-left:0}.table .span9{float:none;width:684px;margin-left:0}.table .span10{float:none;width:764px;margin-left:0}.table .span11{float:none;width:844px;margin-left:0}.table .span12{float:none;width:924px;margin-left:0}.table .span13{float:none;width:1004px;margin-left:0}.table .span14{float:none;width:1084px;margin-left:0}.table .span15{float:none;width:1164px;margin-left:0}.table .span16{float:none;width:1244px;margin-left:0}.table .span17{float:none;width:1324px;margin-left:0}.table .span18{float:none;width:1404px;margin-left:0}.table .span19{float:none;width:1484px;margin-left:0}.table .span20{float:none;width:1564px;margin-left:0}.table .span21{float:none;width:1644px;margin-left:0}.table .span22{float:none;width:1724px;margin-left:0}.table .span23{float:none;width:1804px;margin-left:0}.table .span24{float:none;width:1884px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-tabs>.active>a>[class^="icon-"],.nav-tabs>.active>a>[class*=" icon-"],.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#08c;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#08c;background-color:#0081c2;background-image:linear-gradient(to bottom,#08c,#0077b3);background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 14px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:2px}.btn-small{padding:3px 9px;font-size:12px;line-height:18px}.btn-small [class^="icon-"]{margin-top:0}.btn-mini{padding:2px 6px;font-size:11px;line-height:17px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-image:-moz-linear-gradient(top,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-image:-moz-linear-gradient(top,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px}.btn-group>.btn-mini{font-size:11px}.btn-group>.btn-small{font-size:12px}.btn-group>.btn-large{font-size:16px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical .btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible;color:#777}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;width:100%;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1),0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1),0 1px 10px rgba(0,0,0,0.1);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1),0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,0.1),0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 0 rgba(0,0,0,0.1),0 -1px 10px rgba(0,0,0,0.1);box-shadow:inset 0 1px 0 rgba(0,0,0,0.1),0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse{color:#999}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-image:-moz-linear-gradient(top,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#ccc}.breadcrumb .active{color:#999}.pagination{height:40px;margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:0 14px;line-height:38px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager a,.pager span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a,.pager .next span{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover,.pager .disabled span{color:#999;cursor:default;background-color:#fff}.modal-open .modal .dropdown-menu{z-index:2050}.modal-open .modal .dropdown.open{*z-index:2050}.modal-open .modal .popover{z-index:2060}.modal-open .modal .tooltip{z-index:2080}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-bottom:10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-right:10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow:after{z-index:-1;content:""}.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-top-color:#fff;border-width:10px 10px 0}.popover.top .arrow:after{bottom:-1px;left:-11px;border-top-color:rgba(0,0,0,0.25);border-width:11px 11px 0}.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-right-color:#fff;border-width:10px 10px 10px 0}.popover.right .arrow:after{bottom:-11px;left:-1px;border-right-color:rgba(0,0,0,0.25);border-width:11px 11px 11px 0}.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-bottom-color:#fff;border-width:0 10px 10px}.popover.bottom .arrow:after{top:-1px;left:-11px;border-bottom-color:rgba(0,0,0,0.25);border-width:0 11px 11px}.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-left-color:#fff;border-width:10px 0 10px 10px}.popover.left .arrow:after{right:-1px;bottom:-11px;border-left-color:rgba(0,0,0,0.25);border-width:11px 0 11px 11px}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.label,.badge{font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit p{font-size:18px;font-weight:200;line-height:30px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css new file mode 100644 index 0000000..7efa811 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css @@ -0,0 +1,76 @@ +body { + padding-top: 10px; +} + +.popover { + width: 600px; +} + +.table .progress { + margin-bottom: inherit; +} + +.table-borderless th, .table-borderless td { + border: 0 !important; +} + +.table tbody td.success, li.success, span.success { + background-color: #dff0d8; +} + +.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { + background-color: #f2dede; +} + +.table tbody td.warning, li.warning, span.warning { + background-color: #fcf8e3; +} + +.table tbody td.info { + background-color: #d9edf7; +} + +td.big { + width: 100px; +} + +td.small { +} + +td.codeLine { + font-family: monospace; + white-space: pre; +} + +td span.comment { + color: #888a85; +} + +td span.default { + color: #2e3436; +} + +td span.html { + color: #888a85; +} + +td span.keyword { + color: #2e3436; + font-weight: bold; +} + +pre span.string { + color: #2e3436; +} + +span.success, span.warning, span.danger { + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +#classCoverageDistribution, #classComplexity { + height: 200px; + width: 475px; +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist new file mode 100644 index 0000000..a09c93a --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist @@ -0,0 +1,117 @@ + + + + + Dashboard for {full_path} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +

      Class Coverage Distribution

      +
      +
      +
      +

      Class Complexity

      +
      +
      +
      +
      +
      +

      Top Project Risks

      +
        +{top_project_risks} +
      +
      +
      +

      Least Tested Methods

      +
        +{least_tested_methods} +
      +
      +
      + +
      + + + + + + diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist new file mode 100644 index 0000000..871333e --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist @@ -0,0 +1,58 @@ + + + + + Code Coverage for {full_path} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      + + + + + + + + + + + + + + +{items} + +
       
      Code Coverage
       
      Lines
      Functions and Methods
      Classes and Traits
      +
      +

      Legend

      +

      + Low: 0% to {low_upper_bound}% + Medium: {low_upper_bound}% to {high_lower_bound}% + High: {high_lower_bound}% to 100% +

      +

      + Generated by PHP_CodeCoverage {version} using PHP {php_version}{generator} at {date}. +

      +
      +
      + + + diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist new file mode 100644 index 0000000..4a19a06 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist @@ -0,0 +1,13 @@ + + {icon}{name} + {lines_bar} +
      {lines_executed_percent}
      +
      {lines_number}
      + {methods_bar} +
      {methods_tested_percent}
      +
      {methods_number}
      + {classes_bar} +
      {classes_tested_percent}
      +
      {classes_number}
      + + diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist new file mode 100644 index 0000000..f00d85e --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist @@ -0,0 +1,65 @@ + + + + + Code Coverage for {full_path} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      + + + + + + + + + + + + + + +{items} + +
       
      Code Coverage
       
      Classes and Traits
      Functions and Methods
      Lines
      + + +{lines} + +
      + +
      + + + + + diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist new file mode 100644 index 0000000..7bff4e5 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist @@ -0,0 +1,14 @@ + + {name} + {classes_bar} +
      {classes_tested_percent}
      +
      {classes_number}
      + {methods_bar} +
      {methods_tested_percent}
      +
      {methods_number}
      + {crap} + {lines_bar} +
      {lines_executed_percent}
      +
      {lines_number}
      + + diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png new file mode 100644 index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643 GIT binary patch literal 12799 zcma*OWmH^Ivn@*S;K3nSf_t!#;0f+&pm7Po8`nk}2q8f5;M%x$SdAkd9FAvlc$ zx660V9e3Ox@4WZ^?7jZ%QFGU-T~%||Ug4iK6bbQY@zBuF2$hxOw9wF=A)nUSxR_5@ zEX>HBryGrjyuOFFv$Y4<+|3H@gQfEqD<)+}a~mryD|1U9*I_FOG&F%+Ww{SJ-V2BR zjt<81Ek$}Yb*95D4RS0HCps|uLyovt;P05hchQb-u2bzLtmog&f2}1VlNhxXV);S9 zM2buBg~!q9PtF)&KGRgf3#z7B(hm5WlNClaCWFs!-P!4-u*u5+=+D|ZE9e`KvhTHT zJBnLwGM%!u&vlE%1ytJ=!xt~y_YkFLQb6bS!E+s8l7PiPGSt9xrmg?LV&&SL?J~cI zS(e9TF1?SGyh+M_p@o1dyWu7o7_6p;N6hO!;4~ z2B`I;y`;$ZdtBpvK5%oQ^p4eR2L)BH>B$FQeC*t)c`L71gXHPUa|vyu`Bnz)H$ZcXGve(}XvR!+*8a>BLV;+ryG1kt0=)ytl zNJxFUN{V7P?#|Cp85QTa@(*Q3%K-R(Pkv1N8YU*(d(Y}9?PQ(j;NzWoEVWRD-~H$=f>j9~PN^BM2okI(gY-&_&BCV6RP&I$FnSEM3d=0fCxbxA6~l>54-upTrw zYgX@%m>jsSGi`0cQt6b8cX~+02IghVlNblR7eI;0ps}mpWUcxty1yG56C5rh%ep(X z?)#2d?C<4t-KLc*EAn>>M8%HvC1TyBSoPNg(4id~H8JwO#I)Bf;N*y6ai6K9_bA`4 z_g9(-R;qyH&6I$`b42v|0V3Z8IXN*p*8g$gE98+JpXNY+jXxU0zsR^W$#V=KP z3AEFp@OL}WqwOfsV<)A^UTF4&HF1vQecz?LWE@p^Z2){=KEC_3Iopx_eS42>DeiDG zWMXGbYfG~W7C8s@@m<_?#Gqk;!&)_Key@^0xJxrJahv{B&{^!>TV7TEDZlP|$=ZCz zmX=ZWtt4QZKx**)lQQoW8y-XLiOQy#T`2t}p6l*S`68ojyH@UXJ-b~@tN`WpjF z%7%Yzv807gsO!v=!(2uR)16!&U5~VPrPHtGzUU?2w(b1Xchq}(5Ed^G|SD7IG+kvgyVksU) z(0R)SW1V(>&q2nM%Z!C9=;pTg!(8pPSc%H01urXmQI6Gi^dkYCYfu6b4^tW))b^U+ z$2K&iOgN_OU7n#GC2jgiXU{caO5hZt0(>k+c^(r><#m|#J^s?zA6pi;^#*rp&;aqL zRcZi0Q4HhVX3$ybclxo4FFJW*`IV`)Bj_L3rQe?5{wLJh168Ve1jZv+f1D}f0S$N= zm4i|9cEWz&C9~ZI3q*gwWH^<6sBWuphgy@S3Qy?MJiL>gwd|E<2h9-$3;gT9V~S6r z)cAcmE0KXOwDA5eJ02-75d~f?3;n7a9d_xPBJaO;Z)#@s7gk5$Qn(Fc^w@9c5W0zY z59is0?Mt^@Rolcn{4%)Ioat(kxQH6}hIykSA)zht=9F_W*D#<}N(k&&;k;&gKkWIL z0Of*sP=X(Uyu$Pw;?F@?j{}=>{aSHFcii#78FC^6JGrg-)!)MV4AKz>pXnhVgTgx8 z1&5Y=>|8RGA6++FrSy=__k_imx|z-EI@foKi>tK0Hq2LetjUotCgk2QFXaej!BWYL zJc{fv(&qA7UUJ|AXLc5z*_NW#yWzKtl(c8mEW{A>5Hj^gfZ^HC9lQNQ?RowXjmuCj4!!54Us1=hY z0{@-phvC}yls!PmA~_z>Y&n&IW9FQcj}9(OLO-t^NN$c0o}YksCUWt|DV(MJB%%Sr zdf}8!9ylU2TW!=T{?)g-ojAMKc>3pW;KiZ7f0;&g)k}K^#HBhE5ot)%oxq$*$W@b# zg4p<Ou`ME|Kd1WHK@8 zzLD+0(NHWa`B{em3Ye?@aVsEi>y#0XVZfaFuq#;X5C3{*ikRx7UY4FF{ZtNHNO?A_ z#Q?hwRv~D8fPEc%B5E-ZMI&TAmikl||EERumQCRh7p;)>fdZMxvKq;ky0}7IjhJph zW*uuu*(Y6)S;Od--8uR^R#sb$cmFCnPcj9PPCWhPN;n`i1Q#Qn>ii z{WR|0>8F`vf&#E(c2NsoH=I7Cd-FV|%(7a`i}gZw4N~QFFG2WtS^H%@c?%9UZ+kez z;PwGgg_r6V>Kn5n(nZ40P4qMyrCP3bDkJp@hp6&X3>gzC>=f@Hsen<%I~7W+x@}b> z0}Et*vx_50-q@PIV=(3&Tbm}}QRo*FP2@)A#XX-8jYspIhah`9ukPBr)$8>Tmtg&R z?JBoH17?+1@Y@r>anoKPQ}F8o9?vhcG79Cjv^V6ct709VOQwg{c0Q#rBSsSmK3Q;O zBpNihl3S0_IGVE)^`#94#j~$;7+u870yWiV$@={|GrBmuz4b)*bCOPkaN0{6$MvazOEBxFdKZDlbVvv{8_*kJ zfE6C`4&Kkz<5u%dEdStd85-5UHG5IOWbo8i9azgg#zw-(P1AA049hddAB*UdG3Vn0 zX`OgM+EM|<+KhJ<=k?z~WA5waVj?T9eBdfJGebVifBKS1u<$#vl^BvSg)xsnT5Aw_ZY#}v*LXO#htB>f}x3qDdDHoFeb zAq7;0CW;XJ`d&G*9V)@H&739DpfWYzdQt+Kx_E1K#Cg1EMtFa8eQRk_JuUdHD*2;W zR~XFnl!L2A?48O;_iqCVr1oxEXvOIiN_9CUVTZs3C~P+11}ebyTRLACiJuMIG#`xP zKlC|E(S@QvN+%pBc6vPiQS8KgQAUh75C0a2xcPQDD$}*bM&z~g8+=9ltmkT$;c;s z5_=8%i0H^fEAOQbHXf0;?DN5z-5+1 zDxj50yYkz4ox9p$HbZ|H?8ukAbLE^P$@h}L%i6QVcY>)i!w=hkv2zvrduut%!8>6b zcus3bh1w~L804EZ*s96?GB&F7c5?m?|t$-tp2rKMy>F*=4;w*jW}^;8v`st&8)c; z2Ct2{)?S(Z;@_mjAEjb8x=qAQvx=}S6l9?~H?PmP`-xu;ME*B8sm|!h@BX4>u(xg_ zIHmQzp4Tgf*J}Y=8STR5_s)GKcmgV!$JKTg@LO402{{Wrg>#D4-L%vjmtJ4r?p&$F!o-BOf7ej~ z6)BuK^^g1b#(E>$s`t3i13{6-mmSp7{;QkeG5v}GAN&lM2lQT$@(aQCcFP(%UyZbF z#$HLTqGT^@F#A29b0HqiJsRJAlh8kngU`BDI6 zJUE~&!cQ*&f95Ot$#mxU5+*^$qg_DWNdfu+1irglB7yDglzH()2!@#rpu)^3S8weW z_FE$=j^GTY*|5SH95O8o8W9FluYwB=2PwtbW|JG6kcV^dMVmX(wG+Otj;E$%gfu^K z!t~<3??8=()WQSycsBKy24>NjRtuZ>zxJIED;YXaUz$@0z4rl+TW zWxmvM$%4jYIpO>j5k1t1&}1VKM~s!eLsCVQ`TTjn3JRXZD~>GM z$-IT~(Y)flNqDkC%DfbxaV9?QuWCV&-U1yzrV@0jRhE;)ZO0=r-{s@W?HOFbRHDDV zq;eLo+wOW;nI|#mNf(J?RImB9{YSO2Y`9825Lz#u4(nk3)RGv3X8B(A$TsontJ8L! z9JP^eWxtKC?G8^xAZa1HECx*rp35s!^%;&@Jyk)NexVc)@U4$^X1Dag6`WKs|(HhZ#rzO2KEw3xh~-0<;|zcs0L>OcO#YYX{SN8m6`9pp+ zQG@q$I)T?aoe#AoR@%om_#z=c@ych!bj~lV13Qi-xg$i$hXEAB#l=t7QWENGbma4L zbBf*X*4oNYZUd_;1{Ln_ZeAwQv4z?n9$eoxJeI?lU9^!AB2Y~AwOSq67dT9ADZ)s@ zCRYS7W$Zpkdx$3T>7$I%3EI2ik~m!f7&$Djpt6kZqDWZJ-G{*_eXs*B8$1R4+I}Kf zqniwCI64r;>h2Lu{0c(#Atn)%E8&)=0S4BMhq9$`vu|Ct;^ur~gL`bD>J@l)P$q_A zO7b3HGOUG`vgH{}&&AgrFy%K^>? z>wf**coZ2vdSDcNYSm~dZ(vk6&m6bVKmVgrx-X<>{QzA!)2*L+HLTQz$e8UcB&Djq zl)-%s$ZtUN-R!4ZiG=L0#_P=BbUyH+YPmFl_ogkkQ$=s@T1v}rNnZ^eMaqJ|quc+6 z*ygceDOrldsL30w`H;rNu+IjlS+G~p&0SawXCA1+D zC%cZtjUkLNq%FadtHE?O(yQTP486A{1x<{krq#rpauNQaeyhM3*i0%tBpQHQo-u)x z{0{&KS`>}vf2_}b160XZO2$b)cyrHq7ZSeiSbRvaxnKUH{Q`-P(nL&^fcF2){vhN- zbX&WEjP7?b4A%0y6n_=m%l00uZ+}mCYO(!x?j$+O$*TqoD_Q5EoyDJ?w?^UIa491H zE}87(bR`X;@u#3Qy~9wWdWQIg1`cXrk$x9=ccR|RY1~%{fAJ@uq@J3e872x0v$hmv ze_KcL(wM|n0EOp;t{hKoohYyDmYO;!`7^Lx;0k=PWPGZpI>V5qYlzjSL_(%|mud50 z7#{p97s`U|Sn$WYF>-i{i4`kzlrV6a<}=72q2sAT7Zh{>P%*6B;Zl;~0xWymt10Mo zl5{bmR(wJefJpNGK=fSRP|mpCI-)Nf6?Pv==FcFmpSwF1%CTOucV{yqxSyx4Zws3O z8hr5Uyd%ezIO7?PnEO0T%af#KOiXD$e?V&OX-B|ZX-YsgSs%sv-6U+sLPuz{D4bq| zpd&|o5tNCmpT>(uIbRf?8c}d3IpOb3sn6>_dr*26R#ev<_~vi)wleW$PX|5)$_ z+_|=pi(0D(AB_sjQ;sQQSM&AWqzDO1@NHw;C9cPdXRKRI#@nUW)CgFxzQ1nyd!+h& zcjU!U=&u|>@}R(9D$%lu2TlV>@I2-n@fCr5PrZNVyKWR7hm zWjoy^p7v8m#$qN0K#8jT- zq`mSirDZDa1Jxm;Rg3rAPhC)LcI4@-RvKT+@9&KsR3b0_0zuM!Fg7u>oF>3bzOxZPU&$ab$Z9@ zY)f7pKh22I7ZykL{YsdjcqeN++=0a}elQM-4;Q)(`Ep3|VFHqnXOh14`!Bus& z9w%*EWK6AiAM{s$6~SEQS;A>ey$#`7)khZvamem{P?>k)5&7Sl&&NXKk}o!%vd;-! zpo2p-_h^b$DNBO>{h4JdGB=D>fvGIYN8v&XsfxU~VaefL?q} z3ekM?iOKkCzQHkBkhg=hD!@&(L}FcHKoa zbZ7)H1C|lHjwEb@tu=n^OvdHOo7o+W`0-y3KdP#bb~wM=Vr_gyoEq|#B?$&d$tals ziIs-&7isBpvS|CjC|7C&3I0SE?~`a%g~$PI%;au^cUp@ER3?mn-|vyu!$7MV6(uvt z+CcGuM(Ku2&G0tcRCo7#D$Dirfqef2qPOE5I)oCGzmR5G!o#Q~(k~)c=LpIfrhHQk zeAva6MilEifE7rgP1M7AyWmLOXK}i8?=z2;N=no)`IGm#y%aGE>-FN zyXCp0Sln{IsfOBuCdE*#@CQof%jzuU*jkR*Su3?5t}F(#g0BD0Zzu|1MDes8U7f9; z$JBg|mqTXt`muZ8=Z`3wx$uizZG_7>GI7tcfOHW`C2bKxNOR)XAwRkLOaHS4xwlH4 zDpU29#6wLXI;H?0Se`SRa&I_QmI{zo7p%uveBZ0KZKd9H6@U?YGArbfm)D*^5=&Rp z`k{35?Z5GbZnv>z@NmJ%+sx=1WanWg)8r}C_>EGR8mk(NR$pW<-l8OTU^_u3M@gwS z7}GGa1)`z5G|DZirw;FB@VhH7Dq*0qc=|9lLe{w2#`g+_nt>_%o<~9(VZe=zI*SSz4w43-_o>4E4`M@NPKTWZuQJs)?KXbWp1M zimd5F;?AP(LWcaI-^Sl{`~>tmxsQB9Y$Xi*{Zr#py_+I$vx7@NY`S?HFfS!hUiz$a z{>!&e1(16T!Om)m)&k1W#*d#GslD^4!TwiF2WjFBvi=Ms!ADT)ArEW6zfVuIXcXVk z>AHjPADW+mJzY`_Ieq(s?jbk4iD2Rb8*V3t6?I+E06(K8H!!xnDzO%GB;Z$N-{M|B zeT`jo%9)s%op*XZKDd6*)-^lWO{#RaIGFdBH+;XXjI(8RxpBc~azG1H^2v7c^bkFE zZCVPE+E*Q=FSe8Vm&6|^3ki{9~qafiMAf7i4APZg>b%&5>nT@pHH z%O*pOv(77?ZiT{W zBibx}Q12tRc7Py1NcZTp`Q4ey%T_nj@1WKg5Fz_Rjl4wlJQj)rtp8yL3r!Shy zvZvnmh!tH4T6Js-?vI0<-rzzl{mgT*S0d_7^AU_8gBg^03o-J=p(1o6kww2hx|!%T z-jqp}m^G*W?$!R#M%Ef?&2jYxmx+lXWZszpI4d$pUN`(S)|*c^CgdwY>Fa>> zgGBJhwe8y#Xd*q0=@SLEgPF>+Qe4?%E*v{a`||luZ~&dqMBrRfJ{SDMaJ!s_;cSJp zSqZHXIdc@@XteNySUZs^9SG7xK`8=NBNM)fRVOjw)D^)w%L2OPkTQ$Tel-J)GD3=YXy+F4in(ILy*A3m@3o73uv?JC}Q>f zrY&8SWmesiba0|3X-jmlMT3 z*ST|_U@O=i*sM_*48G)dgXqlwoFp5G6qSM3&%_f_*n!PiT>?cNI)fAUkA{qWnqdMi+aNK_yVQ&lx4UZknAc9FIzVk% zo6JmFH~c{_tK!gt4+o2>)zoP{sR}!!vfRjI=13!z5}ijMFQ4a4?QIg-BE4T6!#%?d&L;`j5=a`4is>U;%@Rd~ zXC~H7eGQhhYWhMPWf9znDbYIgwud(6$W3e>$W4$~d%qoJ z+JE`1g$qJ%>b|z*xCKenmpV$0pM=Gl-Y*LT8K+P)2X#;XYEFF4mRbc~jj?DM@(1e`nL=F4Syv)TKIePQUz)bZ?Bi3@G@HO$Aps1DvDGkYF50O$_welu^cL7;vPiMGho74$;4fDqKbE{U zd1h{;LfM#Fb|Z&uH~Rm_J)R~Vy4b;1?tW_A)Iz#S_=F|~pISaVkCnQ0&u%Yz%o#|! zS-TSg87LUfFSs{tTuM3$!06ZzH&MFtG)X-l7>3)V?Txuj2HyG*5u;EY2_5vU0ujA? zHXh5G%6e3y7v?AjhyX79pnRBVr}RmPmtrxoB7lkxEzChX^(vKd+sLh?SBic=Q)5nA zdz7Mw3_iA>;T^_Kl~?1|5t%GZ;ki_+i>Q~Q1EVdKZ)$Sh3LM@ea&D~{2HOG++7*wF zAC6jW4>fa~!Vp5+$Z{<)Qxb|{unMgCv2)@%3j=7)Zc%U<^i|SAF88s!A^+Xs!OASYT%7;Jx?olg_6NFP1475N z#0s<@E~FI}#LNQ{?B1;t+N$2k*`K$Hxb%#8tRQi*Z#No0J}Pl;HWb){l7{A8(pu#@ zfE-OTvEreoz1+p`9sUI%Y{e5L-oTP_^NkgpYhZjp&ykinnW;(fu1;ttpSsgYM8ABX4dHe_HxU+%M(D=~) zYM}XUJ5guZ;=_ZcOsC`_{CiU$zN3$+x&5C`vX-V3`8&RjlBs^rf00MNYZW+jCd~7N z%{jJuUUwY(M`8$`B>K&_48!Li682ZaRknMgQ3~dnlp8C?__!P2z@=Auv;T^$yrsNy zCARmaA@^Yo2sS%2$`031-+h9KMZsIHfB>s@}>Y(z988e!`%4=EDoAQ0kbk>+lCoK60Mx9P!~I zlq~wf7kcm_NFImt3ZYlE(b3O1K^QWiFb$V^a2Jlwvm(!XYx<`i@ZMS3UwFt{;x+-v zhx{m=m;4dgvkKp5{*lfSN3o^keSpp9{hlXj%=}e_7Ou{Yiw(J@NXuh*;pL6@$HsfB zh?v+r^cp@jQ4EspC#RqpwPY(}_SS$wZ{S959`C25777&sgtNh%XTCo9VHJC-G z;;wi9{-iv+ETiY;K9qvlEc04f;ZnUP>cUL_T*ms``EtGoP^B#Q>n2dSrbAg8a>*Lg zd0EJ^=tdW~7fbcLFsqryFEcy*-8!?;n%;F+8i{eZyCDaiYxghr z$8k>L|2&-!lhvuVdk!r-kpSFl`5F5d4DJr%M4-qOy3gdmQbqF1=aBtRM7)c_Ae?$b8 zQg4c8*KQ{XJmL)1c7#0Yn0#PTMEs4-IHPjkn0!=;JdhMXqzMLeh`yOylXROP- zl#z3+fwM9l3%VN(6R77ua*uI9%hO7l7{+Hcbr(peh;afUK?B4EC09J{-u{mv)+u#? zdKVBCPt`eU@IzL)OXA`Ebu`Xp?u0m%h&X41}FNfnJ*g1!1wcbbpo%F4x!-#R9ft!8{5`Ho}04?FI#Kg zL|k`tF1t_`ywdy8(wnTut>HND(qNnq%Sq=AvvZbXnLx|mJhi!*&lwG2g|edBdVgLy zjvVTKHAx(+&P;P#2Xobo7_RttUi)Nllc}}hX>|N?-u5g7VJ-NNdwYcaOG?NK=5)}` zMtOL;o|i0mSKm(UI_7BL_^6HnVOTkuPI6y@ZLR(H?c1cr-_ouSLp{5!bx^DiKd*Yb z{K78Ci&Twup zTKm)ioN|wcYy%Qnwb)IzbH>W!;Ah5Zdm_jRY`+VRJ2 zhkspZ9hbK3iQD91A$d!0*-1i#%x81|s+SPRmD}d~<1p6!A13(!vABP2kNgqEG z?AMgl^P+iRoIY(9@_I?n1829lGvAsRnHwS~|5vD2+Zi53j<5N4wNn0{q>>jF9*bI) zL$kMXM-awNOElF>{?Jr^tOz1glbwaD-M0OKOlTeW3C!1ZyxRbB>8JDof(O&R1bh%3x#>y2~<>OXO#IIedH0Q`(&&?eo-c~ z>*Ah#3~09unym~UC-UFqqI>{dmUD$Y4@evG#ORLI*{ZM)Jl=e1it!XzY($S3V zLG!Y6fCjE>x6r@5FG1n|8ompSZaJ>9)q6jqU;XxCQk9zV(?C9+i*>w z21+KYt1gXX&0`x3E)hS7I5}snbBzox9C@Xzcr|{B8Hw;SY1$}&BoYKXH^hpjW-RgJ z-Fb}tannKCv>y~^`r|(1Q9;+sZlYf3XPSX|^gR01UFtu$B*R;$sPZdIZShRr>|b@J z;#G{EdoY+O;REEjQ}X7_YzWLO+Ey3>a_KDe1CjSe| z6arqcEZ)CX!8r(si`dqbF$uu&pnf^Np{1f*TdJ`r2;@SaZ z#hb4xlaCA@Pwqj#LlUEe5L{I$k(Zj$d3(~)u(F%&xb8={N9hKxlZIO1ABsM{Mt|)2 zJ^t9Id;?%4PfR4&Ph9B9cFK~@tG3wlFW-0fXZS_L4U*EiAA%+`h%q2^6BCC;t0iO4V=s4Qug{M|iDV@s zC7|ef-dxiR7T&Mpre!%hiUhHM%3Qxi$Lzw6&(Tvlx9QA_7LhYq<(o~=Y>3ka-zrQa zhGpfFK@)#)rtfz61w35^sN1=IFw&Oc!Nah+8@qhJ0UEGr;JplaxOGI82OVqZHsqfX ze1}r{jy;G?&}Da}a7>SCDsFDuzuseeCKof|Dz2BPsP8? zY;a)Tkr2P~0^2BeO?wnzF_Ul-ekY=-w26VnU%U3f19Z-pj&2 z4J_a|o4Dci+MO)mPQIM>kdPG1xydiR9@#8m zh27D7GF{p|a{8({Q-Pr-;#jV{2zHR>lGoFtIfIpoMo?exuQyX_A;;l0AP4!)JEM$EwMInZkj+8*IHP4vKRd zKx_l-i*>A*C@{u%ct`y~s6MWAfO{@FPIX&sg8H{GMDc{4M3%$@c8&RAlw0-R<4DO3 trJqdc$mBpWeznn?E0M$F`|3v=`3%T2A17h;rxP7$%JLd=6(2u;`(N3pt&so# literal 0 HcmV?d00001 diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js new file mode 100644 index 0000000..0e33fb1 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js @@ -0,0 +1,6 @@ +/*! +* Bootstrap.js by @fat & @mdo +* Copyright 2012 Twitter, Inc. +* http://www.apache.org/licenses/LICENSE-2.0.txt +*/ +!function(e){e(function(){"use strict";e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(function(){e("body").on("click.alert.data-api",t,n.prototype.close)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(function(){e("body").on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f=e.Event("slide",{relatedTarget:i[0]});this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u]();if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(function(){e("body").on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=!i.data("modal")&&e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(function(){e("body").on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})})}(window.jQuery),!function(e){"use strict";function r(){i(e(t)).removeClass("open")}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(function(){e("body").on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.remove().css({top:0,left:0,display:"block"}).appendTo(t?this.$element:document.body),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.css(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).remove()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.remove()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.remove(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
      ',trigger:"hover",title:"",delay:0,html:!0}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

      '})}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active a").last()[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(function(){e("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),(e.browser.chrome||e.browser.webkit||e.browser.msie)&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
    • ',minLength:1},e.fn.typeahead.Constructor=t,e(function(){e("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); \ No newline at end of file diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js new file mode 100644 index 0000000..0a7fd0f --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js @@ -0,0 +1,245 @@ +/* + Highcharts JS v2.3.2 (2012-08-31) + + (c) 2009-2011 Torstein Hønsi + + License: www.highcharts.com/license +*/ +(function(){function s(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function la(){for(var a=0,b=arguments,c=b.length,d={};a-1?b.split(".")[1].length:0):a=isNaN(b=N(b))?2:b;var b=a,c=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=f<0?"-":"",a=String(A(f=N(+f||0).toFixed(b))),g=a.length>3?a.length%3:0;return e+(g?a.substr(0,g)+d:"")+a.substr(g).replace(/(\d{3})(?=\d)/g, +"$1"+d)+(b?c+N(f-a).toFixed(b).slice(2):"")}function sa(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function hb(a,b,c,d){var e,c=p(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d=D[ib]&&(i.setMilliseconds(0),i.setSeconds(b>=D[Ua]?0:j*V(i.getSeconds()/j)));if(b>=D[Ua])i[Ab](b>=D[Ja]?0:j*V(i[jb]()/j));if(b>=D[Ja])i[Bb](b>=D[pa]?0:j*V(i[kb]()/ +j));if(b>=D[pa])i[lb](b>=D[Ka]?1:j*V(i[La]()/j));b>=D[Ka]&&(i[Cb](b>=D[ta]?0:j*V(i[Wa]()/j)),h=i[Xa]());b>=D[ta]&&(h-=h%j,i[Db](h));if(b===D[Va])i[lb](i[La]()-i[mb]()+p(d,1));d=1;h=i[Xa]();for(var k=i.getTime(),l=i[Wa](),m=i[La](),i=g?0:(864E5+i.getTimezoneOffset()*6E4)%864E5;kc&&(c=a[b]);return c}function Aa(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Na(a){Za||(Za=S(ia));a&&Za.appendChild(a);Za.innerHTML=""}function $a(a, +b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else L.console&&console.log(c)}function ja(a){return parseFloat(a.toPrecision(14))}function ua(a,b){Oa=p(a,b.animation)}function Gb(){var a=O.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Ya=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,p(c,1),p(g,0),p(h,0),p(i,0))).getTime()};jb=b+"Minutes";kb=b+"Hours";mb=b+"Day";La=b+"Date";Wa=b+"Month";Xa=b+"FullYear";Ab=c+"Minutes";Bb=c+"Hours";lb=c+"Date";Cb=c+"Month"; +Db=c+"FullYear"}function va(){}function Pa(a,b,c){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;c||this.addLabel()}function nb(a,b){this.axis=a;if(b)this.options=b,this.id=b.id;return this}function Hb(a,b,c,d,e){var f=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.alignOptions={align:b.align||(f?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(f?"middle":c?"bottom":"top"),y:p(b.y,f?4:c?14:-6),x:p(b.x,f?c?-6:6:0)};this.textAlign=b.textAlign|| +(f?c?"right":"left":"center")}function ob(){this.init.apply(this,arguments)}function pb(a,b){var c=b.borderWidth,d=b.style,e=A(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape,null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).hide().add();$||this.label.shadow(b.shadow);this.shared=b.shared}function qb(a,b){var c=$?"": +b.chart.zoomType;this.zoomX=/x/.test(c);this.zoomY=/y/.test(c);this.options=b;this.chart=a;this.init(a,b.tooltip)}function rb(a){this.init(a)}function sb(a,b){var c,d=a.series;a.series=null;c=C(O,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=Z(e)?e:[e,e,e,e];this.optionsMarginTop=p(d.marginTop,e[0]);this.optionsMarginRight=p(d.marginRight,e[1]);this.optionsMarginBottom=p(d.marginBottom,e[2]);this.optionsMarginLeft=p(d.marginLeft,e[3]);this.runChartClick=(e=d.events)&&!!e.click;this.callback=b; +this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;this.init(e)}var x,z=document,L=window,K=Math,t=K.round,V=K.floor,wa=K.ceil,v=K.max,P=K.min,N=K.abs,W=K.cos,aa=K.sin,xa=K.PI,ab=xa*2/360,Ba=navigator.userAgent,Ib=L.opera,Ha=/msie/i.test(Ba)&&!Ib,Qa=z.documentMode===8,tb=/AppleWebKit/.test(Ba),bb=/Firefox/.test(Ba),fa=!!z.createElementNS&&!!z.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,Pb=bb&&parseInt(Ba.split("Firefox/")[1],10)<4, +$=!fa&&!Ha&&!!z.createElement("canvas").getContext,Ra,ga=z.documentElement.ontouchstart!==x,Jb={},ub=0,Za,O,cb,Oa,vb,D,Ca=function(){},ia="div",T="none",wb="rgba(192,192,192,"+(fa?1.0E-6:0.0020)+")",zb="millisecond",ib="second",Ua="minute",Ja="hour",pa="day",Va="week",Ka="month",ta="year",Ya,jb,kb,mb,La,Wa,Xa,Ab,Bb,lb,Cb,Db,ba={};L.Highcharts={};cb=function(a,b,c){if(!u(b)||isNaN(b))return"Invalid date";var a=p(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[kb](),g=d[mb](),h=d[La](),i=d[Wa](),j=d[Xa](), +k=O.lang,l=k.weekdays,b={a:l[g].substr(0,3),A:l[g],d:sa(h),e:h,b:k.shortMonths[i],B:k.months[i],m:sa(i+1),y:j.toString().substr(2,2),Y:j,H:sa(f),I:sa(f%12||12),l:f%12||12,M:sa(d[jb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:sa(d.getSeconds()),L:sa(t(b%1E3),3)};for(e in b)a=a.replace("%"+e,b[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Eb.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};D=la(zb,1,ib,1E3,Ua,6E4,Ja,36E5, +pa,864E5,Va,6048E5,Ka,2592E6,ta,31556952E3);vb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length{point.key}
      ',pointFormat:'{series.name}: {point.y}
      ',shadow:!0,shared:$,snap:ga?25:10,style:{color:"#333333",fontSize:"12px",padding:"5px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer", +color:"#909090",fontSize:"10px"}}};var Y=O.plotOptions,X=Y.line;Gb();var qa=function(a){var b=[],c;(function(a){(c=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(a))?b=[A(c[1]),A(c[2]),A(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))&&(b=[A(c[1],16),A(c[2],16),A(c[3],16),1])})(a);return{get:function(c){return b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+ +")":a},brighten:function(a){if(Ga(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=A(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},setOpacity:function(a){b[3]=a;return this}}};va.prototype={init:function(a,b){this.element=b==="span"?S(b):z.createElementNS("http://www.w3.org/2000/svg",b);this.renderer=a;this.attrSetters={}},animate:function(a,b,c){b=p(b,Oa,!0);eb(this);if(b){b=C(b);if(c)b.complete=c;xb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName, +i=this.renderer,j,k=this.attrSetters,l=this.shadows,m,o,r=this;ma(a)&&u(b)&&(c=a,a={},a[c]=b);if(ma(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),r=B(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&(r=parseFloat(r));else for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==x&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text"){for(e=0;em&&/[ \-]/.test(b.innerText)&&(G(b,{width:m+"px",display:"block",whiteSpace:"normal"}),k=m);m=a.fontMetrics(b.style.fontSize).b;q=r<0&&-k;y=o<0&&-l;ea=r*o<0;q+=o*m*(ea?1-h:h);y-=r*m*(j?ea?h:1-h:1);i&&(q-=k*h*(r<0?-1:1),j&&(y-=l*h*(o<0?-1:1)),G(b,{textAlign:g}));this.xCorr=q;this.yCorr=y}G(b,{left:e+q+"px",top:f+y+"px"});this.cTT=M}}else this.alignOnAdd= +!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.inverted,d=this.rotation,e=[];c&&(a+=this.attr("width"),b+=this.attr("height"));(a||b)&&e.push("translate("+a+","+b+")");c?e.push("rotate(90) scale(-1,1)"):d&&e.push("rotate("+d+" "+(this.x||0)+" "+(this.y||0)+")");e.length&&B(this.element,"transform",e.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){a?(this.alignOptions=a,this.alignByTranslate=b,c|| +this.renderer.alignedObjects.push(this)):(a=this.alignOptions,b=this.alignByTranslate);var c=p(c,this.renderer),d=a.align,e=a.verticalAlign,f=(c.x||0)+(a.x||0),g=(c.y||0)+(a.y||0),h={};/^(right|center)$/.test(d)&&(f+=(c.width-(a.width||0))/{right:1,center:2}[d]);h[b?"translateX":"x"]=t(f);/^(bottom|middle)$/.test(e)&&(g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1));h[b?"translateY":"y"]=t(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a= +this.bBox,b=this.renderer,c,d=this.rotation;c=this.element;var e=d*ab;if(!a){if(c.namespaceURI==="http://www.w3.org/2000/svg"||b.forExport){try{a=c.getBBox?s({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(f){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG&&(b=a.width,c=a.height,d))a.width=N(c*aa(e))+N(b*W(e)),a.height=N(c*W(e))+N(b*aa(e));this.bBox=a}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})}, +add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=B(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=A(g);if(c.handleZ)for(c=0;cg||!u(g)&&u(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;E(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a= +this,b=a.element||{},c=a.shadows,d=a.box,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=null;eb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f/g,'').replace(/<(i|em)>/g,'').replace(//g,"").split(//g),d=b.childNodes,e=/style="([^"]+)"/,f=/href="([^"]+)"/,g=B(b,"x"),h=a.styles,i=h&&A(h.width),j=h&&h.lineHeight,k,h=d.length,l=[];h--;)b.removeChild(d[h]);i&&!a.added&&this.box.appendChild(b); +c[c.length-1]===""&&c.pop();n(c,function(c,d){var h,ea=0,q,c=c.replace(//g,"|||");h=c.split("|||");n(h,function(c){if(c!==""||h.length===1){var m={},p=z.createElementNS("http://www.w3.org/2000/svg","tspan");e.test(c)&&B(p,"style",c.match(e)[1].replace(/(;| |^)color([ :])/,"$1fill$2"));f.test(c)&&(B(p,"onclick",'location.href="'+c.match(f)[1]+'"'),G(p,{cursor:"pointer"}));c=(c.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g,"<").replace(/>/g,">");p.appendChild(z.createTextNode(c)); +ea?m.dx=3:m.x=g;if(!ea){if(d){!fa&&a.renderer.forExport&&G(p,{display:"block"});q=L.getComputedStyle&&A(L.getComputedStyle(k,null).getPropertyValue("line-height"));if(!q||isNaN(q)){var n;if(!(n=j))if(!(n=k.offsetHeight))l[d]=b.getBBox?b.getBBox().height:a.renderer.fontMetrics(b.style.fontSize).h,n=t(l[d]-(l[d-1]||0))||18;q=n}B(p,"dy",q)}k=p}B(p,m);b.appendChild(p);ea++;if(i)for(var c=c.replace(/-/g,"- ").split(" "),I=[];c.length||I.length;)delete a.bBox,n=a.getBBox().width,m=n>i,!m||c.length===1? +(c=I,I=[],c.length&&(p=z.createElementNS("http://www.w3.org/2000/svg","tspan"),B(p,{dy:j||16,x:g}),b.appendChild(p),n>i&&(i=n))):(p.removeChild(p.firstChild),I.unshift(c.pop())),c.length&&p.appendChild(z.createTextNode(c.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c),i=0,j,k,l,m,o,a={x1:0,y1:0,x2:0,y2:1},e=C(la("stroke-width",1,"stroke","#999","fill",la("linearGradient",a,"stops",[[0,"#FFF"],[1,"#DDD"]]),"r",3,"padding",3,"style",la("color","black")),e); +l=e.style;delete e.style;f=C(e,la("stroke","#68A","fill",la("linearGradient",a,"stops",[[0,"#FFF"],[1,"#ACF"]])),f);m=f.style;delete f.style;g=C(e,la("stroke","#68A","fill",la("linearGradient",a,"stops",[[0,"#9BD"],[1,"#CDF"]])),g);o=g.style;delete g.style;H(h.element,"mouseenter",function(){h.attr(f).css(m)});H(h.element,"mouseleave",function(){j=[e,f,g][i];k=[l,m,o][i];h.attr(j).css(k)});h.setState=function(a){(i=a)?a===2&&h.attr(g).css(o):h.attr(e).css(l)};return h.on("click",function(){d.call(h)}).attr(e).css(s({cursor:"default"}, +l))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=t(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=t(a[2])+b%2/2);return a},path:function(a){var b={fill:T};Fa(a)?b.d=a:Z(a)&&s(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=Z(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(Z(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e=Z(a)? +a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:T});return e.attr(Z(a)?a:e.crisp(f,a,b,v(c,0),v(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[p(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return u(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:T};arguments.length>1&&s(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f); +f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(t(b),t(c),d,e,f),i=/^url\((.*?)\)$/,j,k;h?(g=this.path(h),s(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&s(g,f)):i.test(a)&&(k=function(a,b){a.attr({width:b[0],height:b[1]});a.alignByTranslate||a.translate(-t(b[0]/2),-t(b[1]/2))},j=a.match(i)[1],a=Jb[j],g=this.image(j).attr({x:b,y:c}),a?k(g,a):(g.attr({width:0, +height:0}),S("img",{onload:function(){k(g,Jb[j]=[this.width,this.height])},src:j})));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+ +c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-1.0E-6,d=e.innerR,h=e.open,i=W(f),j=aa(f),k=W(g),g=aa(g),e=e.end-f');if(b)c=b===ia||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=S(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();E(this,"add");return this},updateTransform:va.prototype.htmlUpdateTransform, +attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,l=this.shadows,m,o=this.attrSetters,r=this;ma(a)&&u(b)&&(c=a,a={},a[c]=b);if(ma(a))c=a,r=c==="strokeWidth"||c==="stroke-width"?this.strokeweight:this[c];else for(c in a)if(d=a[c],m=!1,e=o[c]&&o[c].call(this,d,c),e!==!1&&d!==null){e!==x&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k||(this.symbolAttr(a),k=!0),m=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e= +d.length;for(m=[];e--;)m[e]=Ga(d[e])?t(d[e]*10)-5:d[e]==="Z"?"x":d[e];d=m.join(" ")||"x";f.path=d;if(l)for(e=l.length;e--;)l[e].path=l[e].cutOff?this.cutOffPath(d,l[e].cutOff):d;m=!0}else if(c==="visibility"){if(l)for(e=l.length;e--;)l[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,c="top");g[c]=d;m=!0}else if(c==="zIndex")d&&(g[c]=d),m=!0;else if(c==="width"||c==="height")d=v(0,d),this[c]=d,this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,m=!0;else if(c==="x"||c==="y")this[c]=d, +g[{x:"left",y:"top"}[c]]=d;else if(c==="class")f.className=d;else if(c==="stroke")d=i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ga(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||S(i.prepVML([""]),null,null,f))[c]=d||"solid",this.dashstyle=d,m=!0;else if(c==="fill")h==="SPAN"?g.color=d:(f.filled=d!==T?!0:!1,d=i.color(d,f,c,this),c="fillcolor");else if(h==="shape"&&c==="rotation")this[c]= +d,f.style.left=-t(aa(d*ab)+1)+"px",f.style.top=t(W(d*ab))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),m=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,m=!0;m||(Qa?f[c]=d:B(f,c,d))}return r},clip:function(a){var b=this,c,d=b.element,e=d.parentNode;a?(c=a.members,c.push(b),b.destroyClip=function(){ya(c,b)},e&&e.className==="highcharts-tracker"&&!Qa&&G(d,{visibility:"hidden"}),a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:Qa?"inherit": +"rect(auto)"});return b.css(a)},css:va.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Na(a)},destroy:function(){this.destroyClip&&this.destroyClip();return va.prototype.destroy.apply(this)},empty:function(){for(var a=this.element.childNodes,b=a.length,c;b--;)c=a[b],c.parentNode.removeChild(c)},on:function(a,b){this.element["on"+a]=function(){var a=L.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]= +A(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,l,m,o,r;k&&typeof k.value!=="string"&&(k="x");m=k;if(a){o=p(a.width,3);r=(a.opacity||0.15)/o;for(e=1;e<=3;e++){l=o*2+1-2*e;c&&(m=this.cutOffPath(k.value,l+0.5));j=[''];h=S(g.prepVML(j),null,{left:A(i.left)+(a.offsetX||1),top:A(i.top)+(a.offsetY||1)});if(c)h.cutOff= +l+1;j=[''];S(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}};ka=da(va,ka);var ha={Element:ka,isIE8:Ba.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(ia);e=d.element;e.style.position="relative";a.appendChild(d.element);this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!z.namespaces.hcv)z.namespaces.add("hcv","urn:schemas-microsoft-com:vml"), +z.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(),f=Z(a);return s(e,{members:[],left:f?a.x:a,top:f?a.y:b,width:f?a.width:c,height:f?a.height:d,getCSS:function(a){var b=a.inverted,c=this.top,d=this.left,e=d+this.width,f=c+this.height,c={clip:"rect("+t(b?d:c)+"px,"+t(b?f:e)+"px,"+t(b?e:f)+"px,"+t(b?c:d)+"px)"};!b&& +Qa&&a.element.nodeName!=="IMG"&&s(c,{width:e+"px",height:f+"px"});return c},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=T;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,l,m=a.linearGradient||a.radialGradient,o,r,p,q,y,M="",a=a.stops,u,w=[],I=function(){h=[''];S(e.prepVML(h), +null,null,b)};o=a[0];u=a[a.length-1];o[0]>0&&a.unshift([0,o[1]]);u[0]<1&&a.push([1,u[1]]);n(a,function(a,b){g.test(a[1])?(f=qa(a[1]),k=f.get("rgb"),l=f.get("a")):(k=a[1],l=1);w.push(a[0]*100+"% "+k);b?(p=l,q=k):(r=l,y=k)});if(c==="fill")if(i==="gradient")c=m.x1||m[0]||0,a=m.y1||m[1]||0,o=m.x2||m[2]||0,m=m.y2||m[3]||0,M='angle="'+(90-K.atan((m-a)/(o-c))*180/xa)+'"',I();else{var j=m.r,J=j*2,Q=j*2,t=m.cx,s=m.cy,v=b.radialReference,x,j=function(){v&&(x=d.getBBox(),t+=(v[0]-x.x)/x.width-0.5,s+=(v[1]-x.y)/ +x.height-0.5,J*=v[2]/x.width,Q*=v[2]/x.height);M='src="'+O.global.VMLRadialGradientURL+'" size="'+J+","+Q+'" origin="0.5,0.5" position="'+t+","+s+'" color2="'+y+'" ';I()};d.added?j():H(d,"add",j);j=q}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=qa(a),h=["<",c,' opacity="',f.get("a"),'"/>'],S(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1;j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'), +a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","1&&f.css({left:b,top:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(Z(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,v(c,0),v(d,0)))},invertChild:function(a,b){var c=b.style;G(a,{flip:"x",left:A(c.width)-1,top:A(c.height)-1,rotation:-90})},symbols:{arc:function(a,b, +c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=W(f),d=aa(f),i=W(g),j=aa(g),k=e.innerR,l=0.08/h,m=k&&0.1/k||0;if(g-f===0)return["x"];else 2*xa-g+fj&&(c=!1)):h+k>m&&(h=m-k,d&&h+l0&&b.height>0){f= +C({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=t.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:y}).css(f.style).add();b=[r[1],r[4],p(r[6],r[1])];r=[r[2],r[5],p(r[7],r[2])];c=Ma(b);k=Ma(r);g.align(f,!1,{x:c,y:k,width:za(b)-c,height:za(r)-k});g.show()}else g&&g.hide();return a},destroy:function(){ya(this.axis.plotLinesAndBands,this);Aa(this,this.axis)}};Hb.prototype={destroy:function(){Aa(this, +this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options.formatter.call(this);this.label?this.label.attr({text:b,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(b,0,0).css(this.options.style).attr({align:this.textAlign,rotation:this.options.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.total,0,0,0,1),c=c.translate(0),c=N(g-c),h=d.xAxis[0].translate(this.x)+ +a,d=d.plotHeight,e={x:e?f?g:g-c:h,y:e?d-h-b:f?d-g-c:d-g,width:e?c:b,height:e?b:c};this.label&&this.label.align(this.alignOptions,null,e).attr({visibility:"visible"})}};ob.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:F,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1, +minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#6D869F",fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270, +text:"Y-values"},stackLabels:{enabled:!1,formatter:function(){return this.total},style:F.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{align:"center",x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x": +"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type,f=e==="datetime";this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions=b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.categories=d.categories;this.isLog=e==="logarithmic";this.isLinked=u(d.linkedTo);this.isDatetimeAxis=f;this.tickmarkOffset=d.categories&&d.tickmarkPlacement=== +"between"?0.5:0;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.min=this.max=null;var g,d=this.options.events;a.axes.push(this);a[c?"xAxis":"yAxis"].push(this);this.series=[];if(a.inverted&&c&&this.reversed===x)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;this.addPlotLine=this.addPlotBand=this.addPlotBandOrLine; +for(g in d)H(this,g,d[g]);if(this.isLog)this.val2lin=na,this.lin2val=ca},setOptions:function(a){this.options=C(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],C(O[this.isXAxis?"xAxis":"yAxis"],a))},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=this.dateTimeLabelFormat,d=O.lang.numericSymbols,e=d&&d.length,f,g=a.isLog?b:a.tickInterval;if(a.categories)f= +b;else if(c)f=cb(c,b);else if(e&&g>=1E3)for(;e--&&f===x;)a=Math.pow(1E3,e+1),g>=a&&d[e]!==null&&(f=Ia(b/a,-1)+d[e]);f===x&&(f=b>=1E3?Ia(b,0):Ia(b,-1));return f},getSeriesExtremes:function(){var a=this,b=a.chart,c=a.stacks,d=[],e=[],f;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var h=g.options,i,j,k,l,m,o,r,n,q,y=h.threshold,t,s=[],w=0;a.hasVisibleSeries=!0;if(a.isLog&&y<=0)y=h.threshold=null;if(a.isXAxis){if(h=g.xData,h.length)a.dataMin= +P(p(a.dataMin,h[0]),Ma(h)),a.dataMax=v(p(a.dataMax,h[0]),za(h))}else{var I,J,Q,C=g.cropped,Ea=g.xAxis.getExtremes(),A=!!g.modifyValue;i=h.stacking;a.usePercentage=i==="percent";if(i)m=h.stack,l=g.type+p(m,""),o="-"+l,g.stackKey=l,j=d[l]||[],d[l]=j,k=e[o]||[],e[o]=k;if(a.usePercentage)a.dataMin=0,a.dataMax=99;h=g.processedXData;r=g.processedYData;t=r.length;for(f=0;f=Ea.min&&(h[f-1]||n)<=Ea.max))if(n=q.length)for(;n--;)q[n]!==null&&(s[w++]=q[n]);else s[w++]=q;if(!a.usePercentage&&s.length)a.dataMin=P(p(a.dataMin,s[0]),Ma(s)),a.dataMax=v(p(a.dataMax,s[0]),za(s));if(u(y))if(a.dataMin>=y)a.dataMin=y,a.ignoreMinPadding=!0;else if(a.dataMaxe+this.width)l=!0}else if(c=e,h=k-this.right,gf+this.height)l=!0;return l?null:d.renderer.crispLine(["M",c,g,"L",h,i],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d,b=ja(V(b/a)*a),c=ja(wa(c/a)*a),e=[];b<=c;){e.push(b);b=ja(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e= +this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=t(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=V(b),h,i,j,k,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];fb&&g.push(k),k>c&&(l=!0),k=j}else if(b=ca(b),c=ca(c),a=e[d?"minorTickInterval":"tickInterval"],a=p(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=hb(a, +null,K.pow(10,V(K.log(a)/K.LN10))),g=Sa(this.getLinearTickPositions(a,b,c),na),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.tickPositions,b=this.minorTickInterval,c=[],d,e;if(this.isLog){e=a.length;for(d=1;d= +this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===x&&!this.isLog)u(a.min)||u(a.max)?this.minRange=null:(n(this.series,function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===x||h0||!b.ignoreMaxPadding))b.max+=c*j}b.tickInterval=b.min===b.max||b.min===void 0||b.max===void 0?1:h&&!l&&o===b.linkedParent.options.tickPixelInterval? +b.linkedParent.tickInterval:p(l,r?1:(b.max-b.min)*o/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(a);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!l&&b.tickIntervale&&i.shift(),d.endOnTick?b.max=f:b.max+hb[d]&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this.xOrY,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&&!this.isLinked&&this.options.alignTicks!==!1){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(ea||a===null?a=c:b=a.min&&b<=a.max)j[b]||(j[b]=new Pa(a,b)),y&&j[b].isNew&&j[b].render(c,!0),j[b].isActive=!0,j[b].render(c)}),o&&n(g,function(b,c){if(c%2===0&&b1||N(b-f.y)>1)?function(){e.move(a,b,c,d)}:null},hide:function(){if(!this.isHidden){var a=this.chart.hoverPoints; +this.label.hide();a&&n(a,function(a){a.setState()});this.chart.hoverPoints=null;this.isHidden=!0}},hideCrosshairs:function(){n(this.crosshairs,function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=0,g=0,h,a=oa(a);c=a[0].tooltipPos;c||(n(a,function(a){h=a.series.yAxis;f+=a.plotX;g+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&h?h.top-d.plotTop:0)}),f/=a.length,g/=a.length,c=[e?d.plotWidth-g:f,this.shared&&!e&&a.length>1&&b?b.chartY-d.plotTop:e?d.plotHeight-f:g]); +return Sa(c,t)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i=p(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,l;d<7&&(d=e+j+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,l=!0);k=k&&c<=k+b&&(k=c+f+i));k+b>f+h&&(k=v(f,f+h-b-i));return{x:d,y:k}},refresh:function(a,b){function c(){var a=this.points||oa(this),b=a[0].series,c;c=[b.tooltipHeaderFormatter(a[0].key)];n(a,function(a){b=a.series;c.push(b.tooltipFormatter&& +b.tooltipFormatter(a)||a.point.tooltipFormatter(b.tooltipOptions.pointFormat))});c.push(f.footerFormat||"");return c.join("")}var d=this.chart,e=this.label,f=this.options,g,h,i,j={},k,l=[];k=f.formatter||c;var j=d.hoverPoints,m,o=f.crosshairs;i=this.shared;h=this.getAnchor(a,b);g=h[0];h=h[1];i&&(!a.series||!a.series.noSharedTooltip)?(d.hoverPoints=a,j&&n(j,function(a){a.setState()}),n(a,function(a){a.setState("hover");l.push(a.getLabelConfig())}),j={x:a[0].category,y:a[0].y},j.points=l,a=a[0]):j= +a.getLabelConfig();k=k.call(j);j=a.series;i=i||!j.isCartesian||j.tooltipOutsidePlot||d.isInsidePlot(g,h);k===!1||!i?this.hide():(this.isHidden&&e.show(),e.attr({text:k}),m=f.borderColor||a.color||j.color||"#606060",e.attr({stroke:m}),e=(f.positioner||this.getPosition).call(this,e.width,e.height,{plotX:g,plotY:h}),this.move(t(e.x),t(e.y),g+d.plotLeft,h+d.plotTop),this.isHidden=!1);if(o){o=oa(o);for(e=o.length;e--;)if(i=a.series[e?"yAxis":"xAxis"],o[e]&&i)if(i=i.getPlotLinePath(e?p(a.stackY,a.y):a.x, +1),this.crosshairs[e])this.crosshairs[e].attr({d:i,visibility:"visible"});else{j={"stroke-width":o[e].width||1,stroke:o[e].color||"#C0C0C0",zIndex:o[e].zIndex||2};if(o[e].dashStyle)j.dashstyle=o[e].dashStyle;this.crosshairs[e]=d.renderer.path(i).attr(j).add()}}E(d,"tooltipRefresh",{text:k,x:g+d.plotLeft,y:h+d.plotTop,borderColor:m})},tick:function(){this.tooltipTick&&this.tooltipTick()}};qb.prototype={normalizeMouseEvent:function(a){var b,c,d,a=a||L.event;if(!a.target)a.target=a.srcElement;a=Lb(a); +d=a.touches?a.touches.item(0):a;this.chartPosition=b=Sb(this.chart.container);d.pageX===x?(c=a.x,b=a.y):(c=d.pageX-b.left,b=d.pageY-b.top);return s(a,{chartX:t(c),chartY:t(b)})},getMouseCoordinates:function(a){var b={xAxis:[],yAxis:[]},c=this.chart;n(c.axes,function(d){var e=d.isXAxis;b[e?"xAxis":"yAxis"].push({axis:d,value:d.translate(((c.inverted?!e:e)?a.chartX-c.plotLeft:d.top+d.len-a.chartY)-d.minPixelPadding,!0)})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+ +b.plotTop-a.chartY:a.chartX-b.plotLeft},onmousemove:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f=b.hoverPoint,g=b.hoverSeries,h,i,j=b.chartWidth,k=this.getIndex(a);if(d&&this.options.tooltip.shared&&(!g||!g.noSharedTooltip)){e=[];h=c.length;for(i=0;i +j&&e.splice(h,1);if(e.length&&e[0].plotX!==this.hoverX)d.refresh(e,a),this.hoverX=e[0].plotX}if(g&&g.tracker&&(b=g.tooltipPoints[k])&&b!==f)b.onMouseOver()},resetTracker:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,b=e&&e.shared?b.hoverPoints:d;(a=a&&e&&b)&&oa(b)[0].plotX===x&&(a=!1);if(a)e.refresh(b);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&(e.hide(),e.hideCrosshairs());this.hoverX=null}},setDOMEvents:function(){function a(){if(b.selectionMarker){var f={xAxis:[], +yAxis:[]},g=b.selectionMarker.getBBox(),h=g.x-c.plotLeft,l=g.y-c.plotTop,m;e&&(n(c.axes,function(a){if(a.options.zoomEnabled!==!1){var b=a.isXAxis,d=c.inverted?!b:b,e=a.translate(d?h:c.plotHeight-l-g.height,!0,0,0,1),d=a.translate((d?h+g.width:c.plotHeight-l)-2*a.minPixelPadding,!0,0,0,1);!isNaN(e)&&!isNaN(d)&&(f[b?"xAxis":"yAxis"].push({axis:a,min:P(e,d),max:v(e,d)}),m=!0)}}),m&&E(c,"selection",f,function(a){c.zoom(a)}));b.selectionMarker=b.selectionMarker.destroy()}if(c)G(d,{cursor:"auto"}),c.cancelClick= +e,c.mouseIsDown=e=!1;U(z,ga?"touchend":"mouseup",a)}var b=this,c=b.chart,d=c.container,e,f=b.zoomX&&!c.inverted||b.zoomY&&c.inverted,g=b.zoomY&&!c.inverted||b.zoomX&&c.inverted;b.hideTooltipOnMouseMove=function(a){a=Lb(a);b.chartPosition&&c.hoverSeries&&c.hoverSeries.isCartesian&&!c.isInsidePlot(a.pageX-b.chartPosition.left-c.plotLeft,a.pageY-b.chartPosition.top-c.plotTop)&&b.resetTracker()};b.hideTooltipOnMouseLeave=function(){b.resetTracker();b.chartPosition=null};d.onmousedown=function(d){d=b.normalizeMouseEvent(d); +!ga&&d.preventDefault&&d.preventDefault();c.mouseIsDown=!0;c.cancelClick=!1;c.mouseDownX=b.mouseDownX=d.chartX;b.mouseDownY=d.chartY;H(z,ga?"touchend":"mouseup",a)};var h=function(a){if(!a||!(a.touches&&a.touches.length>1)){a=b.normalizeMouseEvent(a);if(!ga)a.returnValue=!1;var d=a.chartX,h=a.chartY,l=!c.isInsidePlot(d-c.plotLeft,h-c.plotTop);ga&&a.type==="touchstart"&&(B(a.target,"isTracker")?c.runTrackerClick||a.preventDefault():!c.runChartClick&&!l&&a.preventDefault());if(l)dc.plotLeft+c.plotWidth&&(d=c.plotLeft+c.plotWidth),hc.plotTop+c.plotHeight&&(h=c.plotTop+c.plotHeight);if(c.mouseIsDown&&a.type!=="touchstart"&&(e=Math.sqrt(Math.pow(b.mouseDownX-d,2)+Math.pow(b.mouseDownY-h,2)),e>10)){var m=c.isInsidePlot(b.mouseDownX-c.plotLeft,b.mouseDownY-c.plotTop);if(c.hasCartesianSeries&&(b.zoomX||b.zoomY)&&m&&!b.selectionMarker)b.selectionMarker=c.renderer.rect(c.plotLeft,c.plotTop,f?1:c.plotWidth,g?1:c.plotHeight,0).attr({fill:b.options.chart.selectionMarkerFill|| +"rgba(69,114,167,0.25)",zIndex:7}).add();if(b.selectionMarker&&f){var o=d-b.mouseDownX;b.selectionMarker.attr({width:N(o),x:(o>0?0:o)+b.mouseDownX})}b.selectionMarker&&g&&(h-=b.mouseDownY,b.selectionMarker.attr({height:N(h),y:(h>0?0:h)+b.mouseDownY}));m&&!b.selectionMarker&&b.options.chart.panning&&c.pan(d)}if(!l)b.onmousemove(a);return l||!c.hasCartesianSeries}};d.onmousemove=h;H(d,"mouseleave",b.hideTooltipOnMouseLeave);H(z,"mousemove",b.hideTooltipOnMouseMove);d.ontouchstart=function(a){if(b.zoomX|| +b.zoomY)d.onmousedown(a);h(a)};d.ontouchmove=h;d.ontouchend=function(){e&&b.resetTracker()};d.onclick=function(a){var d=c.hoverPoint,e,f,a=b.normalizeMouseEvent(a);a.cancelBubble=!0;if(!c.cancelClick)d&&(B(a.target,"isTracker")||B(a.target.parentNode,"isTracker"))?(e=d.plotX,f=d.plotY,s(d,{pageX:b.chartPosition.left+c.plotLeft+(c.inverted?c.plotWidth-f:e),pageY:b.chartPosition.top+c.plotTop+(c.inverted?c.plotHeight-e:f)}),E(d.series,"click",s(a,{point:d})),d.firePointEvent("click",a)):(s(a,b.getMouseCoordinates(a)), +c.isInsidePlot(a.chartX-c.plotLeft,a.chartY-c.plotTop)&&E(c,"click",a))}},destroy:function(){var a=this.chart,b=a.container;if(a.trackerGroup)a.trackerGroup=a.trackerGroup.destroy();U(b,"mouseleave",this.hideTooltipOnMouseLeave);U(z,"mousemove",this.hideTooltipOnMouseMove);b.onclick=b.onmousedown=b.onmousemove=b.ontouchstart=b.ontouchend=b.ontouchmove=null;clearInterval(this.tooltipInterval)},init:function(a,b){if(!a.trackerGroup)a.trackerGroup=a.renderer.g("tracker").attr({zIndex:9}).add();if(b.enabled)a.tooltip= +new pb(a,b),this.tooltipInterval=setInterval(function(){a&&a.tooltip&&a.tooltip.tick()},32);this.setDOMEvents()}};rb.prototype={init:function(a){var b=this,c=b.options=a.options.legend;if(c.enabled){var d=c.itemStyle,e=p(c.padding,8),f=c.itemMarginTop||0;b.baseline=A(d.fontSize)+3+f;b.itemStyle=d;b.itemHiddenStyle=C(d,c.itemHiddenStyle);b.itemMarginTop=f;b.padding=e;b.initialItemX=e;b.initialItemY=e-5;b.maxItemWidth=0;b.chart=a;b.itemHeight=0;b.lastLineHeight=0;b.render();H(b.chart,"endResize",function(){b.positionCheckboxes()})}}, +colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,g=b?a.color:g;d&&d.css({fill:c});e&&e.attr({stroke:g});f&&f.attr({stroke:g,fill:g})},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine", +"legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Na(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(){var a=this;n(a.allItems,function(b){var c=b.checkbox,d=a.group.alignAttr;c&&G(c,{left:d.translateX+b.legendItemWidth+c.x-20+"px",top:d.translateY+c.y+3+"px"})})},renderItem:function(a){var q;var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=e.symbolWidth,h=e.symbolPadding, +i=b.itemStyle,j=b.itemHiddenStyle,k=b.padding,l=!e.rtl,m=e.width,o=e.itemMarginBottom||0,r=b.itemMarginTop,p=b.initialItemX,n=a.legendItem,y=a.series||a,u=y.options,t=u.showCheckbox;if(!n&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup),y.drawLegendSymbol(b,a),a.legendItem=n=d.text(e.labelFormatter.call(a),l?g+h:-h,b.baseline,e.useHTML).css(C(a.visible?i:j)).attr({align:l?"left":"right",zIndex:2}).add(a.legendGroup),a.legendGroup.on("mouseover",function(){a.setState("hover"); +n.css(b.options.itemHoverStyle)}).on("mouseout",function(){n.css(a.visible?i:j);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):E(a,"legendItemClick",b,c)}),b.colorizeItem(a,a.visible),u&&t))a.checkbox=S("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},e.itemCheckboxStyle,c.container),H(a.checkbox,"click",function(b){E(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})}); +d=n.getBBox();q=a.legendItemWidth=e.itemWidth||g+h+d.width+k+(t?20:0),e=q;b.itemHeight=g=d.height;if(f&&b.itemX-p+e>(m||c.chartWidth-2*k-p))b.itemX=p,b.itemY+=r+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=v(b.maxItemWidth,e);b.lastItemY=r+b.itemY+o;b.lastLineHeight=v(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=r+g+o,b.lastLineHeight=g);b.offsetWidth=m||v(f?b.itemX-p:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i= +a.box,j=a.options,k=a.padding,l=j.borderWidth,m=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup),a.clipRect=c.clipRect(0,0,9999,b.chartHeight),a.contentGroup.clip(a.clipRect);e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&(e=e.concat(a.legendItems||(b.legendType==="point"?a.data:a)))});Fb(e,function(a,b){return(a.options&& +a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight;h=a.handleOverflow(h);if(l||m){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,l||0).attr({stroke:j.borderColor,"stroke-width":l||0,fill:m||T}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth= +g;a.legendHeight=h;n(e,function(b){a.positionItem(b)});f&&d.align(s({width:g,height:h},j),!0,b.spacingBox);b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,i=e.navigation,j=p(i.animation,!0),k=i.arrowSize||12,l=this.nav;e.layout==="horizontal"&&(f/=2);g&&(f=P(f,g));if(a>f){this.clipHeight=c=f-20;this.pageCount=wa(a/c);this.currentPage= +p(this.currentPage,1);this.fullHeight=a;h.attr({height:c});if(!l)this.nav=l=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,k,k).on("click",function(){b.scroll(-1,j)}).add(l),this.pager=d.text("",15,10).css(i.style).add(l),this.down=d.symbol("triangle-down",0,0,k,k).on("click",function(){b.scroll(1,j)}).add(l);b.scroll(0);a=f}else l&&(h.attr({height:c.chartHeight}),l.hide(),this.scrollGroup.attr({translateY:1}));return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+ +a,e=this.clipHeight,f=this.options.navigation,g=f.activeColor,f=f.inactiveColor,h=this.pager,i=this.padding;d>c&&(d=c);if(d>0)b!==x&&ua(b,this.chart),this.nav.attr({translateX:i,translateY:e+7,visibility:"visible"}),this.up.attr({fill:d===1?f:g}).css({cursor:d===1?"default":"pointer"}),h.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?f:g}).css({cursor:d===c?"default":"pointer"}),this.scrollGroup.animate({translateY:-P(e*(d-1),this.fullHeight-e+i)+1}), +h.attr({text:d+"/"+c}),this.currentPage=d}};sb.prototype={initSeries:function(a){var b=this.options.chart,b=new ba[a.type||b.type||b.defaultSeriesType];b.init(this,a);return b},addSeries:function(a,b,c){var d,e=this;a&&(ua(c,e),b=p(b,!0),E(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;b&&e.redraw()}));return d},isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!== +!1&&n(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.tracker,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,i=c.length,j=i,k=this.renderer,l=k.isHidden();ua(a,this);for(l&&this.cloneRenderTo();j--;)if(a=c[j],a.isDirty&&a.options.stacking){g=!0;break}if(g)for(j=i;j--;)if(a=c[j],a.options.stacking)a.isDirty=!0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend= +!1;if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,E(a,"afterSetExtremes",a.getExtremes());if(a.isDirty||h||g)a.redraw(),h=!0})}h&&this.drawChartBox();n(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.resetTracker&&d.resetTracker(!0);k.draw();E(this,"redraw");l&&this.cloneRenderTo(!0)},showLoading:function(a){var b= +this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=S(ia,{className:"highcharts-loading"},s(d.style,{left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px",zIndex:10,display:T}),this.container),this.loadingSpan=S("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)G(c,{opacity:0,display:""}),xb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a= +this.options,b=this.loadingDiv;b&&xb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){G(b,{display:T})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;dP(e.dataMin,e.min)&&c19?this.containerHeight:400)},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Na(b),delete this.renderToClone):(c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),G(b,{position:"absolute",top:"-9999px",display:"block"}),z.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a, +b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+ub++;if(ma(a))this.renderTo=a=z.getElementById(a);a||$a(13,!0);a.innerHTML="";a.offsetWidth||this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=S(ia,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},s({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0},b.style),this.renderToClone||a);this.renderer=b.forExport? +new ra(a,c,d,!0):new Ra(a,c,d);$&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart,b=a.spacingTop,c=a.spacingRight,d=a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,i=this.optionsMarginRight,j=this.optionsMarginBottom,k=this.chartTitleOptions,l=this.chartSubtitleOptions,m=this.options.legend,o=p(m.margin,10),r=m.x,t=m.y,q=m.align,y=m.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&& +!u(this.optionsMarginTop))if(l=v(this.title&&!k.floating&&!k.verticalAlign&&k.y||0,this.subtitle&&!l.floating&&!l.verticalAlign&&l.y||0))this.plotTop=v(this.plotTop,l+p(k.margin,15)+b);if(f.display&&!m.floating)if(q==="right"){if(!u(i))this.marginRight=v(this.marginRight,f.legendWidth-r+o+c)}else if(q==="left"){if(!u(h))this.plotLeft=v(this.plotLeft,f.legendWidth+r+o+a)}else if(y==="top"){if(!u(g))this.plotTop=v(this.plotTop,f.legendHeight+t+o+b)}else if(y==="bottom"&&!u(j))this.marginBottom=v(this.marginBottom, +f.legendHeight-t+o+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});u(h)||(this.plotLeft+=e[3]);u(g)||(this.plotTop+=e[0]);u(j)||(this.marginBottom+=e[2]);u(i)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g=c.width||db(d,"width"),h=c.height||db(d,"height"),a=a?a.target:L;if(g&&h&&(a===L||a===z)){if(g!==b.containerWidth|| +h!==b.containerHeight)clearTimeout(e),e=setTimeout(function(){b.resize(g,h,!1)},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart,d=b.renderTo,e;H(L,"resize",a);H(b,"destroy",function(){U(L,"resize",a)})},resize:function(a,b,c){var d=this,e,f,g=d.resetZoomButton,h=d.title,i=d.subtitle,j;d.isResizing+=1;j=function(){d&&E(d,"endResize",null,function(){d.isResizing-=1})};ua(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(u(a))d.chartWidth=e=t(a);if(u(b))d.chartHeight= +f=t(b);G(d.container,{width:e+"px",height:f+"px"});d.renderer.setSize(e,f,c);d.plotWidth=e-d.plotLeft-d.marginRight;d.plotHeight=f-d.plotTop-d.marginBottom;d.maxTicks=null;n(d.axes,function(a){a.isDirty=!0;a.setScale()});n(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();a=d.spacingBox;h&&h.align(null,null,a);i&&i.align(null,null,a);g&&g.align(null,null,d[g.alignTo]);d.redraw(c);d.oldChartHeight=null;E(d,"resize");Oa===!1?j():setTimeout(j,Oa&&Oa.duration||500)}, +setChartSize:function(){var a=this.inverted,b=this.chartWidth,c=this.chartHeight,d=this.options.chart,e=d.spacingTop,f=d.spacingRight,g=d.spacingBottom,h=d.spacingLeft,i,j,k,l;this.plotLeft=i=t(this.plotLeft);this.plotTop=j=t(this.plotTop);this.plotWidth=k=t(b-i-this.marginRight);this.plotHeight=l=t(c-j-this.marginBottom);this.plotSizeX=a?l:k;this.plotSizeY=a?k:l;this.plotBorderWidth=a=d.plotBorderWidth||0;this.spacingBox={x:h,y:e,width:b-h-f,height:c-e-g};this.plotBox={x:i,y:j,width:k,height:l}; +this.clipBox={x:a/2,y:a/2,width:this.plotSizeX-a,height:this.plotSizeY-a};n(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart,b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=p(this.optionsMarginTop,a.spacingTop);this.marginRight=p(this.optionsMarginRight,b);this.marginBottom=p(this.optionsMarginBottom,c);this.plotLeft=p(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart, +b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,l=a.plotBackgroundImage,m=a.plotBorderWidth||0,o,p=this.plotLeft,n=this.plotTop,q=this.plotWidth,y=this.plotHeight,t=this.plotBox,u=this.clipRect,w=this.clipBox;o=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-o,d-o));else{e={fill:j||T};if(i)e.stroke=a.borderColor,e["stroke-width"]=i; +this.chartBackground=b.rect(o/2,o/2,c-o,d-o,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?f.animate(t):this.plotBackground=b.rect(p,n,q,y,0).attr({fill:k}).add().shadow(a.plotShadow);if(l)h?h.animate(t):this.plotBGImage=b.image(l,p,n,q,y).add();u?u.animate({width:w.width,height:w.height}):this.clipRect=b.clipRect(w);if(m)g?g.animate(g.crisp(null,p,n,q,y)):this.plotBorder=b.rect(p,n,q,y,0,m).attr({stroke:a.plotBorderColor,"stroke-width":m,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a= +this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"],function(g){c=ba[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=ba[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,d=d.credits,f;a.setTitle();a.legend=new rb(a);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins(); +a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(b){var d=s(e.style,b.style),f=A(d.left)+a.plotLeft,j=A(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,j).attr({zIndex:2}).css(d).add()});if(d.enabled&&!a.credits)f=d.href,a.credits=c.text(d.text,0,0).on("click",function(){if(f)location.href=f}).attr({align:d.position.align, +zIndex:8}).css(d.style).add().align(d.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;if(a!==null){E(a,"destroy");U(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,tracker,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&(a[b]=c.destroy())});if(d)d.innerHTML= +"",U(d),f&&Na(d),d=null;for(e in a)delete a[e];a=a.options=null}},firstRender:function(){var a=this,b=a.options,c=a.callback;if(!fa&&L==L.top&&z.readyState!=="complete"||$&&!L.canvg)$?Mb.push(function(){a.firstRender()},b.global.canvasToolsURL):z.attachEvent("onreadystatechange",function(){z.detachEvent("onreadystatechange",a.firstRender);z.readyState==="complete"&&a.firstRender()});else{a.getContainer();E(a,"init");if(Highcharts.RangeSelector&&b.rangeSelector.enabled)a.rangeSelector=new Highcharts.RangeSelector(a); +a.resetMargins();a.setChartSize();a.propFromSeries();a.getAxes();n(b.series||[],function(b){a.initSeries(b)});if(Highcharts.Scroller&&(b.navigator.enabled||b.scrollbar.enabled))a.scroller=new Highcharts.Scroller(a);a.tracker=new qb(a,b);a.render();a.renderer.draw();c&&c.apply(a,[a]);n(a.callbacks,function(b){b.apply(a,[a])});a.cloneRenderTo(!0);E(a,"load")}},init:function(a){var b=this.options.chart,c;b.reflow!==!1&&H(this,"load",this.initReflow);if(a)for(c in a)H(this,c,a[c]);this.xAxis=[];this.yAxis= +[];this.animation=$?!1:p(b.animation,!0);this.setSize=this.resize;this.pointCount=0;this.counters=new Eb;this.firstRender()}};sb.prototype.callbacks=[];var Ta=function(){};Ta.prototype={init:function(a,b,c){var d=a.chart.counters;this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint)b=a.chart.options.colors,this.color=this.color||b[d.color++],d.wrapColor(b.length);a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=typeof a;this.config=a;if(d=== +"number"||a===null)this.y=a;else if(typeof a[0]==="number")this.x=a[0],this.y=a[1];else if(d==="object"&&typeof a.length!=="number"){s(this,a);this.options=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers=!0}else if(typeof a[0]==="string")this.name=a[0],this.y=a[1];if(this.x===x)this.x=b===x?c.autoIncrement():b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;if(b&&(this.setState(),ya(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut(); +if(this.graphic||this.dataLabel)U(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,tracker,dataLabel,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c= +this,d=c.series.chart,a=p(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=a;c.setState(a&&"select");b||n(d.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=!1,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(){var a=this.series,b=a.chart,c=b.tooltip,d=b.hoverPoint;if(d&&d!==this)d.onMouseOut();this.firePointEvent("mouseOver");c&&(!c.shared||a.noSharedTooltip)&&c.refresh(this);this.setState("hover");b.hoverPoint=this},onMouseOut:function(){var a= +this.series.chart,b=a.hoverPoints;if(!b||Rb(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=a.match(/\{(series|point)\.[a-zA-Z]+\}/g),e=/[{\.}]/,f,g,h,i,j={y:0,open:0,high:0,low:0,close:0,percentage:1,total:1};c.valuePrefix=c.valuePrefix||c.yPrefix;c.valueDecimals=c.valueDecimals||c.yDecimals;c.valueSuffix=c.valueSuffix||c.ySuffix;for(i in d)g=d[i],ma(g)&&g!==a&&(h=(" "+g).split(e),f={point:this,series:b}[h[1]], +h=h[2],f===this&&j.hasOwnProperty(h)?(f=j[h]?h:"value",f=(c[f+"Prefix"]||"")+Ia(this[h],p(c[f+"Decimals"],-1))+(c[f+"Suffix"]||"")):f=f[h],a=a.replace(g,f));return a},update:function(a,b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,i=h.length,j=e.chart,b=p(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);Z(a)&&(e.getAttribs(),f&&f.attr(d.pointAttr[e.state]));for(g=0;ga+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions,d=c[this.type],e=a.data;a.data=null;c=C(d,c.series,a);c.data=a.data=e;this.tooltipOptions=C(b.tooltip,c.tooltip); +d.marker===null&&delete c.marker;return c},getColor:function(){var a=this.options,b=this.chart.options.colors,c=this.chart.counters;this.color=a.color||!a.colorByPoint&&b[c.color++]||"gray";c.wrapColor(b.length)},getSymbol:function(){var a=this.options.marker,b=this.chart,c=b.options.symbols,b=b.counters;this.symbol=a.symbol||c[b.symbol++];if(/^url/.test(this.symbol))a.radius=0;b.wrapSymbol(c.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer, +f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&&c.enabled)b=c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).attr(this.pointAttr[""]).add(f)},addPoint:function(a,b,c,d){var e=this.data,f=this.graph,g=this.area,h=this.chart,i=this.xData,j=this.yData,k=f&&f.shift||0,l=this.options.data,m=this.pointClass.prototype;ua(d,h);if(f&&c)f.shift=k+1;if(g){if(c)g.shift= +k+1;g.isArea=!0}b=p(b,!0);d={series:this};m.applyOptions.apply(d,[a]);i.push(d.x);j.push(m.toYData?m.toYData.call(d):d.y);l.push(a);c&&(e[0]&&e[0].remove?e[0].remove(!1):(e.shift(),i.shift(),j.shift(),l.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&h.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.initialColor,f=this.chart,g=null,h=this.xAxis,i,j=this.pointClass.prototype;this.xIncrement=null;this.pointRange=h&&h.categories?1:d.pointRange;if(u(e))f.counters.color= +e;var e=[],k=[],l=a?a.length:[],m=(i=this.pointArrayMap)&&i.length;if(l>(d.turboThreshold||1E3)){for(i=0;g===null&&ik||this.forceCrop))if(a=i.getExtremes(),i=a.min,k=a.max,b[d-1]k)b=[],c=[];else if(b[0]k){for(a=0;a=i){e=v(0,a-1);break}for(;ak){f=a+1;break}b=b.slice(e,f);c=c.slice(e,f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===x||d=0&&c<=d;)i[c++]=g}this.tooltipPoints=i}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=this.xAxis,e=d&&d.options.type==="datetime",f;if(e&& +!c)for(f in D)if(D[f]>=d.closestPointRange){c=b.dateTimeLabelFormats[f];break}return b.headerFormat.replace("{point.key}",e?cb(c,a):a).replace("{series.name}",this.name).replace("{series.color}",this.color)},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&E(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&& +E(this,"mouseOut");c&&!a.stickyTracking&&!c.shared&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!Z(e))e=Y[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h],e=c[h+"m"],a||(c[h]=a=d.clipRect(s(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX}, +e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()},e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip());setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,l=this.options.marker,m,o=this.markerGroup;if(l.enabled||this._hasPointMarkers)for(f= +b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,i=g.marker||{},a=l.enabled&&i.enabled===x||i.enabled,m=c.isInsidePlot(d,e,c.inverted),a&&e!==x&&!isNaN(e))if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=p(i.symbol,this.symbol),j=i.indexOf("url")===0,k)k.attr({visibility:m?fa?"inherit":"visible":"hidden"}).animate(s({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else if(m&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(o)},convertAttribs:function(a,b,c,d){var e= +this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=p(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=Y[a.type].marker?a.options.marker:a.options,c=b.states,d=c.hover,e,f=a.color,g={stroke:f,fill:f},h=a.points||[],i=[],j,k=a.pointAttrToOptions,l;a.options.marker?(d.radius=d.radius||b.radius+2,d.lineWidth=d.lineWidth||b.lineWidth+1):d.color=d.color||qa(d.color||f).brighten(d.brightness).get();i[""]=a.convertAttribs(b,g);n(["hover","select"],function(b){i[b]= +a.convertAttribs(c[b],i[""])});a.pointAttr=i;for(f=h.length;f--;){g=h[f];if((b=g.options&&g.options.marker||g.options)&&b.enabled===!1)b.radius=0;e=a.options.colorByPoint;if(g.options)for(l in k)u(b[k[l]])&&(e=!0);if(e){b=b||{};j=[];c=b.states||{};e=c.hover=c.hover||{};if(!a.options.marker)e.color=qa(e.color||g.color).brighten(e.brightness||d.brightness).get();j[""]=a.convertAttribs(s({color:g.color},b),i[""]);j.hover=a.convertAttribs(c.hover,i.hover,j[""]);j.select=a.convertAttribs(c.select,i.select, +j[""])}else j=i;g.pointAttr=j}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(Ba),d,e,f=a.data||[],g,h,i;E(a,"destroy");U(a);n(["xAxis","yAxis"],function(b){if(i=a[b])ya(i.series,a),i.isDirty=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);n("area,graph,dataLabelsGroup,group,markerGroup,tracker,trackerGroup".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())}); +if(b.hoverSeries===a)b.hoverSeries=null;ya(b.series,a);for(h in a)delete a[h]},drawDataLabels:function(){var a=this,b=a.options,c=b.dataLabels;if(c.enabled||a._hasPointLabels){var d,e,f=a.points,g,h,i,j,k=a.chart,l=k.renderer,m=k.inverted,o=a.type,r=b.stacking,s=o==="column"||o==="bar",q=c.verticalAlign===null,y=c.y===null,v=l.fontMetrics(c.style.fontSize),A=v.h,w=v.b;s&&(v={top:w,middle:w-A/2,bottom:-A+w},r?(q&&(c=C(c,{verticalAlign:"middle"})),y&&(c=C(c,{y:v[c.verticalAlign]}))):q?c=C(c,{verticalAlign:"top"}): +y&&(c=C(c,{y:v[c.verticalAlign]})));j=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",6);h=c;n(f,function(f){var n,q,y,v,B=f.dataLabel;c=h;(g=f.options)&&g.dataLabels&&(c=C(c,g.dataLabels));if(v=c.enabled)n=f.barX&&f.barX+f.barW/2||p(f.plotX,-999),q=p(f.plotY,-999),y=c.y===null?f.y>=b.threshold?-A+w:w:c.y,d=(m?k.plotWidth-q:n)+c.x,e=t((m?k.plotHeight-n:q)+y);a.isCartesian&&!k.isInsidePlot(d-c.x,e)&&(v=!1);if(B&&!v)f.dataLabel=B.destroy();else if(v){n=c.align;var z;i=c.formatter.call(f.getLabelConfig(), +c);o==="column"&&(d+={left:-1,right:1}[n]*f.barW/2||0);!r&&m&&f.y<0&&(n="right",d-=10);c.style.color=p(c.color,c.style.color,a.color,"black");if(B)B.attr({text:i}).animate({x:d,y:e});else if(u(i)){B={align:n,fill:c.backgroundColor,stroke:c.borderColor,"stroke-width":c.borderWidth,r:c.borderRadius||0,rotation:c.rotation,padding:c.padding,zIndex:1};for(z in B)B[z]===x&&delete B[z];B=f.dataLabel=l[c.rotation?"text":"label"](i,d,e,null,null,null,c.useHTML,!0).attr(B).css(c.style).add(j).shadow(c.shadow)}if(s&& +b.stacking&&B)z=f.barX,n=f.barY,q=f.barW,f=f.barH,B.align(c,null,{x:m?k.plotWidth-n-f:z,y:m?k.plotHeight-z-q:n,width:m?f:q,height:m?q:f})}})}},getSegmentPath:function(a){var b=this,c=[];n(a,function(d,e){b.getPointSpline?c.push.apply(c,b.getPointSpline(a,d,e)):(c.push(e?"L":"M"),e&&b.options.step&&c.push(d.plotX,a[e-1].plotY),c.push(d.plotX,d.plotY))});return c},getGraphPath:function(){var a=this,b=[],c,d=[];n(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints= +d;return a.graphPath=b},drawGraph:function(){var a=this.options,b=this.graph,c=this.group,d=a.lineColor||this.color,e=a.lineWidth,f=a.dashStyle,g=this.getGraphPath();if(b)eb(b),b.animate({d:g});else if(e){b={stroke:d,"stroke-width":e,zIndex:1};if(f)b.dashstyle=f;this.graph=this.chart.renderer.path(g).attr(b).add(c).shadow(a.shadow)}},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};n(["group","trackerGroup","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})} +var b=this,c=b.chart;H(c,"resize",a);H(b,"destroy",function(){U(c,"resize",a)});a();b.invertGroups=a},plotGroup:function(a,b,c,d,e){var f=this[a],g=this.chart,h=this.xAxis,i=this.yAxis;f||(this[a]=f=g.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f.translate(h?h.left:g.plotLeft,i?i.top:g.plotTop);return f},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate,e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group", +"series",e,f,h);this.markerGroup=this.plotGroup("markerGroup","markers",e,f,h);this.drawDataLabels();d&&this.animate(!0);this.getAttribs();b.inverted=a.inverted;this.drawGraph&&this.drawGraph();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&&!g&&(b.clip(a.clipRect),this.trackerGroup&&this.trackerGroup.clip(a.clipRect));d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered= +!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:this.xAxis.left,translateY:this.yAxis.top}));this.translate();this.setTooltipPoints(!0);this.render();b&&E(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,d[a]&&d[a].enabled===!1||(a&&(b=d[a].lineWidth||b+1),c&&!c.dashstyle&&c.attr({"stroke-width":b},a?0: +500))},setVisible:function(a,b){var c=this.chart,d=this.legendItem,e=this.group,f=this.tracker,g=this.dataLabelsGroup,h=this.markerGroup,i,j=this.points,k=c.options.chart.ignoreHiddenSeries;i=this.visible;i=(this.visible=a=a===x?!i:a)?"show":"hide";if(e)e[i]();if(h)h[i]();if(f)f[i]();else if(j)for(e=j.length;e--;)if(f=j[e],f.tracker)f.tracker[i]();if(g)g[i]();d&&c.legend.colorizeItem(this,a);this.isDirty=!0;this.options.stacking&&n(c.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0}); +if(k)c.isDirtyBox=!0;b!==!1&&c.redraw();E(this,i)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===x?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;E(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.renderer,h=f.options.tooltip.snap,i=a.tracker,j=b.cursor,j=j&&{cursor:j},k=a.singlePoints,l=this.isCartesian&&this.plotGroup("trackerGroup", +null,"visible",b.zIndex||1,f.trackerGroup),m;if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-h,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+h,d[m-1]);for(m=0;m=0;d--)da&&i>e?(i=v(a,e),k=2*e-i):ig&&k>e?(k=v(g,e),i=2*e-k):kw?g-w:A-(f<=A?w:0));s(c,{barX:h,barY:i,barW:y,barH:j,pointWidth:q});c.shapeType="rect";c.shapeArgs=f=b.renderer.Element.prototype.crisp.call(0,e,h,i,y,j);e%2&&(f.y-=1,f.height+=1);c.trackerArgs=N(j)<3&&C(c.shapeArgs,{height:6,y:i-3})})},getSymbol:Ca,drawLegendSymbol:F.prototype.drawLegendSymbol, +drawGraph:Ca,drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d,e;n(a.points,function(f){var g=f.plotY;if(g!==x&&!isNaN(g)&&f.y!==null)d=f.graphic,e=f.shapeArgs,d?(eb(d),d.animate(C(e))):f.graphic=d=c[f.shapeType](e).attr(f.pointAttr[f.selected?"select":""]).add(a.group).shadow(b.shadow,null,b.stacking&&!b.borderRadius)})},drawTracker:function(){var a=this,b=a.chart,c=b.renderer,d,e,f=+new Date,g=a.options,h=g.cursor,i=h&&{cursor:h},j=a.isCartesian&&a.plotGroup("trackerGroup",null, +"visible",g.zIndex||1,b.trackerGroup),k,l,m;n(a.points,function(h){e=h.tracker;d=h.trackerArgs||h.shapeArgs;l=h.plotY;m=!a.isCartesian||l!==x&&!isNaN(l);delete d.strokeWidth;if(h.y!==null&&m)e?e.attr(d):h.tracker=c[h.shapeType](d).attr({isTracker:f,fill:wb,visibility:a.visible?"visible":"hidden"}).on(ga?"touchstart":"mouseover",function(c){k=c.relatedTarget||c.fromElement;if(b.hoverSeries!==a&&B(k,"isTracker")!==f)a.onMouseOver();h.onMouseOver()}).on("mouseout",function(b){if(!g.stickyTracking&&(k= +b.relatedTarget||b.toElement,B(k,"isTracker")!==f))a.onMouseOut()}).css(i).add(h.group||j)})},animate:function(a){var b=this,c=b.points,d=b.options;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs,g=b.yAxis,h=d.threshold;c&&(c.attr({height:0,y:u(h)?g.getThreshold(h):g.translate(g.getExtremes().min,0,1,0,1)}),c.animate({height:a.height,y:a.y},d.animation))}),b.animate=null},remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});R.prototype.remove.apply(a, +arguments)}});ba.column=Ca;Y.bar=C(Y.column,{dataLabels:{align:"left",x:5,y:null,verticalAlign:"middle"}});ha=da(Ca,{type:"bar",inverted:!0});ba.bar=ha;Y.scatter=C(X,{lineWidth:0,states:{hover:{lineWidth:0}},tooltip:{headerFormat:'{series.name}
      ',pointFormat:"x: {point.x}
      y: {point.y}
      "}});ha=da(R,{type:"scatter",sorted:!1,translate:function(){var a=this;R.prototype.translate.apply(a);n(a.points,function(b){b.shapeType= +"circle";b.shapeArgs={x:b.plotX,y:b.plotY,r:a.chart.options.tooltip.snap}})},drawTracker:function(){for(var a=this,b=a.options.cursor,b=b&&{cursor:b},c=a.points,d=c.length,e;d--;)if(e=c[d].graphic)e.element._i=d;a._hasTracking?a._hasTracking=!0:a.markerGroup.attr({isTracker:!0}).on(ga?"touchstart":"mouseover",function(b){a.onMouseOver();if(b.target._i!==x)c[b.target._i].onMouseOver()}).on("mouseout",function(){if(!a.options.stickyTracking)a.onMouseOut()}).css(b)}});ba.scatter=ha;Y.pie=C(X,{borderColor:"#FFFFFF", +borderWidth:1,center:["50%","50%"],colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name},y:5},legendType:"point",marker:null,size:"75%",showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}}});X={type:"pie",isCartesian:!1,pointClass:da(Ta,{init:function(){Ta.prototype.init.apply(this,arguments);var a=this,b;s(a,{visible:a.visible!==!1,name:p(a.name,"Slice")});b=function(){a.slice()};H(a,"select",b);H(a,"unselect",b);return a},setVisible:function(a){var b= +this.series,c=b.chart,d=this.tracker,e=this.dataLabel,f=this.connector,g=this.shadowGroup,h;h=(this.visible=a=a===x?!this.visible:a)?"show":"hide";this.group[h]();if(d)d[h]();if(e)e[h]();if(f)f[h]();if(g)g[h]();this.legendItem&&c.legend.colorizeItem(this,a);if(!b.isDirty&&b.options.ignoreHiddenPoint)b.isDirty=!0,c.redraw()},slice:function(a,b,c){var d=this.series.chart,e=this.slicedTranslation;ua(c,d);p(b,!0);a=this.sliced=u(a)?a:!this.sliced;a={translateX:a?e[0]:d.plotLeft,translateY:a?e[1]:d.plotTop}; +this.group.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},getColor:function(){this.initialColor=this.chart.counters.color},animate:function(){var a=this;n(a.points,function(b){var c=b.graphic,b=b.shapeArgs,d=-xa/2;c&&(c.attr({r:0,start:d,end:d}),c.animate({r:b.r,start:b.start,end:b.end},a.options.animation))});a.animate=null},setData:function(a,b){R.prototype.setData.call(this,a,!1);this.processData(); +this.generatePoints();p(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=b.plotWidth,d=b.plotHeight,a=a.center.concat([a.size,a.innerSize||0]),e=P(c,d),f;return Sa(a,function(a,b){return(f=/%$/.test(a))?[c,d,e,e][b]*A(a)/100:a})},translate:function(){this.generatePoints();var a=0,b=-0.25,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g=this.chart,h,i,j,k=this.points,l=2*xa,m,o,p,s=c.dataLabels.distance,q=c.ignoreHiddenPoint;this.center=f=this.getCenter();this.getX= +function(a,b){j=K.asin((a-f[1])/(f[2]/2+s));return f[0]+(b?-1:1)*W(j)*(f[2]/2+s)};n(k,function(b){a+=q&&!b.visible?0:b.y});n(k,function(c){m=a?c.y/a:0;h=t(b*l*1E3)/1E3;if(!q||c.visible)b+=m;i=t(b*l*1E3)/1E3;c.shapeType="arc";c.shapeArgs={x:f[0],y:f[1],r:f[2]/2,innerR:f[3]/2,start:h,end:i};j=(i+h)/2;c.slicedTranslation=Sa([W(j)*d+g.plotLeft,aa(j)*d+g.plotTop],t);o=W(j)*f[2]/2;p=aa(j)*f[2]/2;c.tooltipPos=[f[0]+o*0.7,f[1]+p*0.7];c.labelPos=[f[0]+o+W(j)*s,f[1]+p+aa(j)*s,f[0]+o+W(j)*e,f[1]+p+aa(j)*e,f[0]+ +o,f[1]+p,s<0?"center":j0,r=[[],[]],s,q,t,u,v=2,w;if(d.enabled||this._hasPointLabels){R.prototype.drawDataLabels.apply(this);n(a,function(a){a.dataLabel&&r[a.labelPos[7]0){for(w=m-l-j;w<=m+l+j;w+=a)x.push(w);t=x.length;if(C>t){h=[].concat(A);h.sort(u);for(w=C;w--;)h[w].rank=w;for(w=C;w--;)A[w].rank>=t&& +A.splice(w,1);C=A.length}for(w=0;w0){if(q=B.pop(),z=q.i,q=q.y,s>q&&x[z+1]!==null||s=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
      a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
      t
      ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
      ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
      ",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

      ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
      ","
      "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
      ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist new file mode 100644 index 0000000..95ac783 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist @@ -0,0 +1,11 @@ +
      + + + + + + + + + + diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node.php new file mode 100644 index 0000000..481e309 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node.php @@ -0,0 +1,380 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Base class for nodes in the code coverage information tree. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +abstract class PHP_CodeCoverage_Report_Node implements Countable +{ + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $path; + + /** + * @var array + */ + protected $pathArray; + + /** + * @var PHP_CodeCoverage_Report_Node + */ + protected $parent; + + /** + * @var string + */ + protected $id; + + /** + * Constructor. + * + * @param string $name + * @param PHP_CodeCoverage_Report_Node $parent + */ + public function __construct($name, PHP_CodeCoverage_Report_Node $parent = NULL) + { + if (substr($name, -1) == '/') { + $name = substr($name, 0, -1); + } + + $this->name = $name; + $this->parent = $parent; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return string + */ + public function getId() + { + if ($this->id === NULL) { + $parent = $this->getParent(); + + if ($parent === NULL) { + $this->id = 'index'; + } else { + $parentId = $parent->getId(); + + if ($parentId == 'index') { + $this->id = str_replace(':', '_', $this->name); + } else { + $this->id = $parentId . '_' . $this->name; + } + } + } + + return $this->id; + } + + /** + * @return string + */ + public function getPath() + { + if ($this->path === NULL) { + if ($this->parent === NULL) { + $this->path = $this->name; + } else { + $this->path = $this->parent->getPath() . '/' . $this->name; + } + } + + return $this->path; + } + + /** + * @return array + */ + public function getPathAsArray() + { + if ($this->pathArray === NULL) { + if ($this->parent === NULL) { + $this->pathArray = array(); + } else { + $this->pathArray = $this->parent->getPathAsArray(); + } + + $this->pathArray[] = $this; + } + + return $this->pathArray; + } + + /** + * @return PHP_CodeCoverage_Report_Node + */ + public function getParent() + { + return $this->parent; + } + + /** + * Returns the percentage of classes that has been tested. + * + * @param boolean $asString + * @return integer + */ + public function getTestedClassesPercent($asString = TRUE) + { + return PHP_CodeCoverage_Util::percent( + $this->getNumTestedClasses(), + $this->getNumClasses(), + $asString + ); + } + + /** + * Returns the percentage of traits that has been tested. + * + * @param boolean $asString + * @return integer + */ + public function getTestedTraitsPercent($asString = TRUE) + { + return PHP_CodeCoverage_Util::percent( + $this->getNumTestedTraits(), + $this->getNumTraits(), + $asString + ); + } + + /** + * Returns the percentage of traits that has been tested. + * + * @param boolean $asString + * @return integer + * @since Method available since Release 1.2.0 + */ + public function getTestedClassesAndTraitsPercent($asString = TRUE) + { + return PHP_CodeCoverage_Util::percent( + $this->getNumTestedClassesAndTraits(), + $this->getNumClassesAndTraits(), + $asString + ); + } + + /** + * Returns the percentage of methods that has been tested. + * + * @param boolean $asString + * @return integer + */ + public function getTestedMethodsPercent($asString = TRUE) + { + return PHP_CodeCoverage_Util::percent( + $this->getNumTestedMethods(), + $this->getNumMethods(), + $asString + ); + } + + /** + * Returns the percentage of executed lines. + * + * @param boolean $asString + * @return integer + */ + public function getLineExecutedPercent($asString = TRUE) + { + return PHP_CodeCoverage_Util::percent( + $this->getNumExecutedLines(), + $this->getNumExecutableLines(), + $asString + ); + } + + /** + * Returns the number of classes and traits. + * + * @return integer + * @since Method available since Release 1.2.0 + */ + public function getNumClassesAndTraits() + { + return $this->getNumClasses() + $this->getNumTraits(); + } + + /** + * Returns the number of tested classes and traits. + * + * @return integer + * @since Method available since Release 1.2.0 + */ + public function getNumTestedClassesAndTraits() + { + return $this->getNumTestedClasses() + $this->getNumTestedTraits(); + } + + /** + * Returns the classes and traits of this node. + * + * @return array + * @since Method available since Release 1.2.0 + */ + public function getClassesAndTraits() + { + return array_merge($this->getClasses(), $this->getTraits()); + } + + /** + * Returns the classes of this node. + * + * @return array + */ + abstract public function getClasses(); + + /** + * Returns the traits of this node. + * + * @return array + */ + abstract public function getTraits(); + + /** + * Returns the functions of this node. + * + * @return array + */ + abstract public function getFunctions(); + + /** + * Returns the LOC/CLOC/NCLOC of this node. + * + * @return array + */ + abstract public function getLinesOfCode(); + + /** + * Returns the number of executable lines. + * + * @return integer + */ + abstract public function getNumExecutableLines(); + + /** + * Returns the number of executed lines. + * + * @return integer + */ + abstract public function getNumExecutedLines(); + + /** + * Returns the number of classes. + * + * @return integer + */ + abstract public function getNumClasses(); + + /** + * Returns the number of tested classes. + * + * @return integer + */ + abstract public function getNumTestedClasses(); + + /** + * Returns the number of traits. + * + * @return integer + */ + abstract public function getNumTraits(); + + /** + * Returns the number of tested traits. + * + * @return integer + */ + abstract public function getNumTestedTraits(); + + /** + * Returns the number of methods. + * + * @return integer + */ + abstract public function getNumMethods(); + + /** + * Returns the number of tested methods. + * + * @return integer + */ + abstract public function getNumTestedMethods(); + + /** + * Returns the number of functions. + * + * @return integer + */ + abstract public function getNumFunctions(); + + /** + * Returns the number of tested functions. + * + * @return integer + */ + abstract public function getNumTestedFunctions(); +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Directory.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Directory.php new file mode 100644 index 0000000..808e4aa --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Directory.php @@ -0,0 +1,512 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Represents a directory in the code coverage information tree. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_Node_Directory extends PHP_CodeCoverage_Report_Node implements IteratorAggregate +{ + /** + * @var PHP_CodeCoverage_Report_Node[] + */ + protected $children = array(); + + /** + * @var PHP_CodeCoverage_Report_Node_Directory[] + */ + protected $directories = array(); + + /** + * @var PHP_CodeCoverage_Report_Node_File[] + */ + protected $files = array(); + + /** + * @var array + */ + protected $classes; + + /** + * @var array + */ + protected $traits; + + /** + * @var array + */ + protected $functions; + + /** + * @var array + */ + protected $linesOfCode = NULL; + + /** + * @var integer + */ + protected $numFiles = -1; + + /** + * @var integer + */ + protected $numExecutableLines = -1; + + /** + * @var integer + */ + protected $numExecutedLines = -1; + + /** + * @var integer + */ + protected $numClasses = -1; + + /** + * @var integer + */ + protected $numTestedClasses = -1; + + /** + * @var integer + */ + protected $numTraits = -1; + + /** + * @var integer + */ + protected $numTestedTraits = -1; + + /** + * @var integer + */ + protected $numMethods = -1; + + /** + * @var integer + */ + protected $numTestedMethods = -1; + + /** + * @var integer + */ + protected $numFunctions = -1; + + /** + * @var integer + */ + protected $numTestedFunctions = -1; + + /** + * Returns the number of files in/under this node. + * + * @return integer + */ + public function count() + { + if ($this->numFiles == -1) { + $this->numFiles = 0; + + foreach ($this->children as $child) { + $this->numFiles += count($child); + } + } + + return $this->numFiles; + } + + /** + * Returns an iterator for this node. + * + * @return RecursiveIteratorIterator + */ + public function getIterator() + { + return new RecursiveIteratorIterator( + new PHP_CodeCoverage_Report_Node_Iterator($this), + RecursiveIteratorIterator::SELF_FIRST + ); + } + + /** + * Adds a new directory. + * + * @param string $name + * @return PHP_CodeCoverage_Report_Node_Directory + */ + public function addDirectory($name) + { + $directory = new PHP_CodeCoverage_Report_Node_Directory($name, $this); + + $this->children[] = $directory; + $this->directories[] = &$this->children[count($this->children) - 1]; + + return $directory; + } + + /** + * Adds a new file. + * + * @param string $name + * @param array $coverageData + * @param array $testData + * @param boolean $cacheTokens + * @return PHP_CodeCoverage_Report_Node_File + * @throws PHP_CodeCoverage_Exception + */ + public function addFile($name, array $coverageData, array $testData, $cacheTokens) + { + $file = new PHP_CodeCoverage_Report_Node_File( + $name, $this, $coverageData, $testData, $cacheTokens + ); + + $this->children[] = $file; + $this->files[] = &$this->children[count($this->children) - 1]; + + $this->numExecutableLines = -1; + $this->numExecutedLines = -1; + + return $file; + } + + /** + * Returns the directories in this directory. + * + * @return array + */ + public function getDirectories() + { + return $this->directories; + } + + /** + * Returns the files in this directory. + * + * @return array + */ + public function getFiles() + { + return $this->files; + } + + /** + * Returns the child nodes of this node. + * + * @return array + */ + public function getChildNodes() + { + return $this->children; + } + + /** + * Returns the classes of this node. + * + * @return array + */ + public function getClasses() + { + if ($this->classes === NULL) { + $this->classes = array(); + + foreach ($this->children as $child) { + $this->classes = array_merge( + $this->classes, $child->getClasses() + ); + } + } + + return $this->classes; + } + + /** + * Returns the traits of this node. + * + * @return array + */ + public function getTraits() + { + if ($this->traits === NULL) { + $this->traits = array(); + + foreach ($this->children as $child) { + $this->traits = array_merge( + $this->traits, $child->getTraits() + ); + } + } + + return $this->traits; + } + + /** + * Returns the functions of this node. + * + * @return array + */ + public function getFunctions() + { + if ($this->functions === NULL) { + $this->functions = array(); + + foreach ($this->children as $child) { + $this->functions = array_merge( + $this->functions, $child->getFunctions() + ); + } + } + + return $this->functions; + } + + /** + * Returns the LOC/CLOC/NCLOC of this node. + * + * @return array + */ + public function getLinesOfCode() + { + if ($this->linesOfCode === NULL) { + $this->linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0); + + foreach ($this->children as $child) { + $linesOfCode = $child->getLinesOfCode(); + + $this->linesOfCode['loc'] += $linesOfCode['loc']; + $this->linesOfCode['cloc'] += $linesOfCode['cloc']; + $this->linesOfCode['ncloc'] += $linesOfCode['ncloc']; + } + } + + return $this->linesOfCode; + } + + /** + * Returns the number of executable lines. + * + * @return integer + */ + public function getNumExecutableLines() + { + if ($this->numExecutableLines == -1) { + $this->numExecutableLines = 0; + + foreach ($this->children as $child) { + $this->numExecutableLines += $child->getNumExecutableLines(); + } + } + + return $this->numExecutableLines; + } + + /** + * Returns the number of executed lines. + * + * @return integer + */ + public function getNumExecutedLines() + { + if ($this->numExecutedLines == -1) { + $this->numExecutedLines = 0; + + foreach ($this->children as $child) { + $this->numExecutedLines += $child->getNumExecutedLines(); + } + } + + return $this->numExecutedLines; + } + + /** + * Returns the number of classes. + * + * @return integer + */ + public function getNumClasses() + { + if ($this->numClasses == -1) { + $this->numClasses = 0; + + foreach ($this->children as $child) { + $this->numClasses += $child->getNumClasses(); + } + } + + return $this->numClasses; + } + + /** + * Returns the number of tested classes. + * + * @return integer + */ + public function getNumTestedClasses() + { + if ($this->numTestedClasses == -1) { + $this->numTestedClasses = 0; + + foreach ($this->children as $child) { + $this->numTestedClasses += $child->getNumTestedClasses(); + } + } + + return $this->numTestedClasses; + } + + /** + * Returns the number of traits. + * + * @return integer + */ + public function getNumTraits() + { + if ($this->numTraits == -1) { + $this->numTraits = 0; + + foreach ($this->children as $child) { + $this->numTraits += $child->getNumTraits(); + } + } + + return $this->numTraits; + } + + /** + * Returns the number of tested traits. + * + * @return integer + */ + public function getNumTestedTraits() + { + if ($this->numTestedTraits == -1) { + $this->numTestedTraits = 0; + + foreach ($this->children as $child) { + $this->numTestedTraits += $child->getNumTestedTraits(); + } + } + + return $this->numTestedTraits; + } + + /** + * Returns the number of methods. + * + * @return integer + */ + public function getNumMethods() + { + if ($this->numMethods == -1) { + $this->numMethods = 0; + + foreach ($this->children as $child) { + $this->numMethods += $child->getNumMethods(); + } + } + + return $this->numMethods; + } + + /** + * Returns the number of tested methods. + * + * @return integer + */ + public function getNumTestedMethods() + { + if ($this->numTestedMethods == -1) { + $this->numTestedMethods = 0; + + foreach ($this->children as $child) { + $this->numTestedMethods += $child->getNumTestedMethods(); + } + } + + return $this->numTestedMethods; + } + + /** + * Returns the number of functions. + * + * @return integer + */ + public function getNumFunctions() + { + if ($this->numFunctions == -1) { + $this->numFunctions = 0; + + foreach ($this->children as $child) { + $this->numFunctions += $child->getNumFunctions(); + } + } + + return $this->numFunctions; + } + + /** + * Returns the number of tested functions. + * + * @return integer + */ + public function getNumTestedFunctions() + { + if ($this->numTestedFunctions == -1) { + $this->numTestedFunctions = 0; + + foreach ($this->children as $child) { + $this->numTestedFunctions += $child->getNumTestedFunctions(); + } + } + + return $this->numTestedFunctions; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php new file mode 100644 index 0000000..a5b0154 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php @@ -0,0 +1,721 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Represents a file in the code coverage information tree. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_Node_File extends PHP_CodeCoverage_Report_Node +{ + /** + * @var array + */ + protected $coverageData; + + /** + * @var array + */ + protected $testData; + + /** + * @var array + */ + protected $ignoredLines; + + /** + * @var integer + */ + protected $numExecutableLines = 0; + + /** + * @var integer + */ + protected $numExecutedLines = 0; + + /** + * @var array + */ + protected $classes = array(); + + /** + * @var array + */ + protected $traits = array(); + + /** + * @var array + */ + protected $functions = array(); + + /** + * @var array + */ + protected $linesOfCode = array(); + + /** + * @var integer + */ + protected $numTestedTraits = 0; + + /** + * @var integer + */ + protected $numTestedClasses = 0; + + /** + * @var integer + */ + protected $numMethods = NULL; + + /** + * @var integer + */ + protected $numTestedMethods = NULL; + + /** + * @var integer + */ + protected $numTestedFunctions = NULL; + + /** + * @var array + */ + protected $startLines = array(); + + /** + * @var array + */ + protected $endLines = array(); + + /** + * @var boolean + */ + protected $cacheTokens; + + /** + * Constructor. + * + * @param string $name + * @param PHP_CodeCoverage_Report_Node $parent + * @param array $coverageData + * @param array $testData + * @param boolean $cacheTokens + * @throws PHP_CodeCoverage_Exception + */ + public function __construct($name, PHP_CodeCoverage_Report_Node $parent, array $coverageData, array $testData, $cacheTokens) + { + if (!is_bool($cacheTokens)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + parent::__construct($name, $parent); + + $this->coverageData = $coverageData; + $this->testData = $testData; + $this->ignoredLines = PHP_CodeCoverage_Util::getLinesToBeIgnored( + $this->getPath(), $cacheTokens + ); + $this->cacheTokens = $cacheTokens; + + $this->calculateStatistics(); + } + + /** + * Returns the number of files in/under this node. + * + * @return integer + */ + public function count() + { + return 1; + } + + /** + * Returns the code coverage data of this node. + * + * @return array + */ + public function getCoverageData() + { + return $this->coverageData; + } + + /** + * Returns the test data of this node. + * + * @return array + */ + public function getTestData() + { + return $this->testData; + } + + /** + * @return array + */ + public function getIgnoredLines() + { + return $this->ignoredLines; + } + + /** + * Returns the classes of this node. + * + * @return array + */ + public function getClasses() + { + return $this->classes; + } + + /** + * Returns the traits of this node. + * + * @return array + */ + public function getTraits() + { + return $this->traits; + } + + /** + * Returns the functions of this node. + * + * @return array + */ + public function getFunctions() + { + return $this->functions; + } + + /** + * Returns the LOC/CLOC/NCLOC of this node. + * + * @return array + */ + public function getLinesOfCode() + { + return $this->linesOfCode; + } + + /** + * Returns the number of executable lines. + * + * @return integer + */ + public function getNumExecutableLines() + { + return $this->numExecutableLines; + } + + /** + * Returns the number of executed lines. + * + * @return integer + */ + public function getNumExecutedLines() + { + return $this->numExecutedLines; + } + + /** + * Returns the number of classes. + * + * @return integer + */ + public function getNumClasses() + { + return count($this->classes); + } + + /** + * Returns the number of tested classes. + * + * @return integer + */ + public function getNumTestedClasses() + { + return $this->numTestedClasses; + } + + /** + * Returns the number of traits. + * + * @return integer + */ + public function getNumTraits() + { + return count($this->traits); + } + + /** + * Returns the number of tested traits. + * + * @return integer + */ + public function getNumTestedTraits() + { + return $this->numTestedTraits; + } + + /** + * Returns the number of methods. + * + * @return integer + */ + public function getNumMethods() + { + if ($this->numMethods === NULL) { + $this->numMethods = 0; + + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numMethods++; + } + } + } + + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numMethods++; + } + } + } + } + + return $this->numMethods; + } + + /** + * Returns the number of tested methods. + * + * @return integer + */ + public function getNumTestedMethods() + { + if ($this->numTestedMethods === NULL) { + $this->numTestedMethods = 0; + + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0 && + $method['coverage'] == 100) { + $this->numTestedMethods++; + } + } + } + + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0 && + $method['coverage'] == 100) { + $this->numTestedMethods++; + } + } + } + } + + return $this->numTestedMethods; + } + + /** + * Returns the number of functions. + * + * @return integer + */ + public function getNumFunctions() + { + return count($this->functions); + } + + /** + * Returns the number of tested functions. + * + * @return integer + */ + public function getNumTestedFunctions() + { + if ($this->numTestedFunctions === NULL) { + $this->numTestedFunctions = 0; + + foreach ($this->functions as $function) { + if ($function['executableLines'] > 0 && + $function['coverage'] == 100) { + $this->numTestedFunctions++; + } + } + } + + return $this->numTestedFunctions; + } + + /** + * Calculates coverage statistics for the file. + */ + protected function calculateStatistics() + { + if ($this->cacheTokens) { + $tokens = PHP_Token_Stream_CachingFactory::get($this->getPath()); + } else { + $tokens = new PHP_Token_Stream($this->getPath()); + } + + $this->processClasses($tokens); + $this->processTraits($tokens); + $this->processFunctions($tokens); + $this->linesOfCode = $tokens->getLinesOfCode(); + unset($tokens); + + for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) { + if (isset($this->startLines[$lineNumber])) { + // Start line of a class. + if (isset($this->startLines[$lineNumber]['className'])) { + $currentClass = &$this->startLines[$lineNumber]; + } + + // Start line of a trait. + else if (isset($this->startLines[$lineNumber]['traitName'])) { + $currentTrait = &$this->startLines[$lineNumber]; + } + + // Start line of a method. + else if (isset($this->startLines[$lineNumber]['methodName'])) { + $currentMethod = &$this->startLines[$lineNumber]; + } + + // Start line of a function. + else if (isset($this->startLines[$lineNumber]['functionName'])) { + $currentFunction = &$this->startLines[$lineNumber]; + } + } + + if (!isset($this->ignoredLines[$lineNumber]) && + isset($this->coverageData[$lineNumber]) && + $this->coverageData[$lineNumber] !== NULL) { + if (isset($currentClass)) { + $currentClass['executableLines']++; + } + + if (isset($currentTrait)) { + $currentTrait['executableLines']++; + } + + if (isset($currentMethod)) { + $currentMethod['executableLines']++; + } + + if (isset($currentFunction)) { + $currentFunction['executableLines']++; + } + + $this->numExecutableLines++; + + if (count($this->coverageData[$lineNumber]) > 0 || + isset($this->ignoredLines[$lineNumber])) { + if (isset($currentClass)) { + $currentClass['executedLines']++; + } + + if (isset($currentTrait)) { + $currentTrait['executedLines']++; + } + + if (isset($currentMethod)) { + $currentMethod['executedLines']++; + } + + if (isset($currentFunction)) { + $currentFunction['executedLines']++; + } + + $this->numExecutedLines++; + } + } + + if (isset($this->endLines[$lineNumber])) { + // End line of a class. + if (isset($this->endLines[$lineNumber]['className'])) { + unset($currentClass); + } + + // End line of a trait. + else if (isset($this->endLines[$lineNumber]['traitName'])) { + unset($currentTrait); + } + + // End line of a method. + else if (isset($this->endLines[$lineNumber]['methodName'])) { + unset($currentMethod); + } + + // End line of a function. + else if (isset($this->endLines[$lineNumber]['functionName'])) { + unset($currentFunction); + } + } + } + + foreach ($this->traits as &$trait) { + foreach ($trait['methods'] as &$method) { + if ($method['executableLines'] > 0) { + $method['coverage'] = ($method['executedLines'] / + $method['executableLines']) * 100; + } else { + $method['coverage'] = 100; + } + + $method['crap'] = $this->crap( + $method['ccn'], $method['coverage'] + ); + + $trait['ccn'] += $method['ccn']; + } + + if ($trait['executableLines'] > 0) { + $trait['coverage'] = ($trait['executedLines'] / + $trait['executableLines']) * 100; + } else { + $trait['coverage'] = 100; + } + + if ($trait['coverage'] == 100) { + $this->numTestedClasses++; + } + + $trait['crap'] = $this->crap( + $trait['ccn'], $trait['coverage'] + ); + } + + foreach ($this->classes as &$class) { + foreach ($class['methods'] as &$method) { + if ($method['executableLines'] > 0) { + $method['coverage'] = ($method['executedLines'] / + $method['executableLines']) * 100; + } else { + $method['coverage'] = 100; + } + + $method['crap'] = $this->crap( + $method['ccn'], $method['coverage'] + ); + + $class['ccn'] += $method['ccn']; + } + + if ($class['executableLines'] > 0) { + $class['coverage'] = ($class['executedLines'] / + $class['executableLines']) * 100; + } else { + $class['coverage'] = 100; + } + + if ($class['coverage'] == 100) { + $this->numTestedClasses++; + } + + $class['crap'] = $this->crap( + $class['ccn'], $class['coverage'] + ); + } + } + + /** + * @param PHP_Token_Stream $tokens + */ + protected function processClasses(PHP_Token_Stream $tokens) + { + $classes = $tokens->getClasses(); + unset($tokens); + + $link = $this->getId() . '.html#'; + + foreach ($classes as $className => $class) { + $this->classes[$className] = array( + 'className' => $className, + 'methods' => array(), + 'startLine' => $class['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => 0, + 'coverage' => 0, + 'crap' => 0, + 'package' => $class['package'], + 'link' => $link . $class['startLine'] + ); + + $this->startLines[$class['startLine']] = &$this->classes[$className]; + $this->endLines[$class['endLine']] = &$this->classes[$className]; + + foreach ($class['methods'] as $methodName => $method) { + $this->classes[$className]['methods'][$methodName] = array( + 'methodName' => $methodName, + 'signature' => $method['signature'], + 'startLine' => $method['startLine'], + 'endLine' => $method['endLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => $method['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $method['startLine'] + ); + + $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName]; + $this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName]; + } + } + } + + /** + * @param PHP_Token_Stream $tokens + */ + protected function processTraits(PHP_Token_Stream $tokens) + { + $traits = $tokens->getTraits(); + unset($tokens); + + $link = $this->getId() . '.html#'; + + foreach ($traits as $traitName => $trait) { + $this->traits[$traitName] = array( + 'traitName' => $traitName, + 'methods' => array(), + 'startLine' => $trait['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => 0, + 'coverage' => 0, + 'crap' => 0, + 'package' => $trait['package'], + 'link' => $link . $trait['startLine'] + ); + + $this->startLines[$trait['startLine']] = &$this->traits[$traitName]; + $this->endLines[$trait['endLine']] = &$this->traits[$traitName]; + + foreach ($trait['methods'] as $methodName => $method) { + $this->traits[$traitName]['methods'][$methodName] = array( + 'methodName' => $methodName, + 'signature' => $method['signature'], + 'startLine' => $method['startLine'], + 'endLine' => $method['endLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => $method['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $method['startLine'] + ); + + $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName]; + $this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName]; + } + } + } + + /** + * @param PHP_Token_Stream $tokens + */ + protected function processFunctions(PHP_Token_Stream $tokens) + { + $functions = $tokens->getFunctions(); + unset($tokens); + + $link = $this->getId() . '.html#'; + + foreach ($functions as $functionName => $function) { + $this->functions[$functionName] = array( + 'functionName' => $functionName, + 'signature' => $function['signature'], + 'startLine' => $function['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => $function['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $function['startLine'] + ); + + $this->startLines[$function['startLine']] = &$this->functions[$functionName]; + $this->endLines[$function['endLine']] = &$this->functions[$functionName]; + } + } + + /** + * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code + * based on its cyclomatic complexity and percentage of code coverage. + * + * @param integer $ccn + * @param float $coverage + * @return string + * @since Method available since Release 1.2.0 + */ + protected function crap($ccn, $coverage) + { + if ($coverage == 0) { + return (string)pow($ccn, 2) + $ccn; + } + + if ($coverage >= 95) { + return (string)$ccn; + } + + return sprintf( + '%01.2F', pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Iterator.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Iterator.php new file mode 100644 index 0000000..6c8dd05 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Iterator.php @@ -0,0 +1,148 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Recursive iterator for PHP_CodeCoverage_Report_Node object graphs. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_Node_Iterator implements RecursiveIterator +{ + /** + * @var integer + */ + protected $position; + + /** + * @var PHP_CodeCoverage_Report_Node[] + */ + protected $nodes; + + /** + * Constructor. + * + * @param PHP_CodeCoverage_Report_Node_Directory $node + */ + public function __construct(PHP_CodeCoverage_Report_Node_Directory $node) + { + $this->nodes = $node->getChildNodes(); + } + + /** + * Rewinds the Iterator to the first element. + * + */ + public function rewind() + { + $this->position = 0; + } + + /** + * Checks if there is a current element after calls to rewind() or next(). + * + * @return boolean + */ + public function valid() + { + return $this->position < count($this->nodes); + } + + /** + * Returns the key of the current element. + * + * @return integer + */ + public function key() + { + return $this->position; + } + + /** + * Returns the current element. + * + * @return PHPUnit_Framework_Test + */ + public function current() + { + return $this->valid() ? $this->nodes[$this->position] : NULL; + } + + /** + * Moves forward to next element. + * + */ + public function next() + { + $this->position++; + } + + /** + * Returns the sub iterator for the current element. + * + * @return PHP_CodeCoverage_Report_Node_Iterator + */ + public function getChildren() + { + return new PHP_CodeCoverage_Report_Node_Iterator( + $this->nodes[$this->position] + ); + } + + /** + * Checks whether the current element has children. + * + * @return boolean + */ + public function hasChildren() + { + return $this->nodes[$this->position] instanceof PHP_CodeCoverage_Report_Node_Directory; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/PHP.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/PHP.php new file mode 100644 index 0000000..5d17e86 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/PHP.php @@ -0,0 +1,74 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Uses serialize() to write a PHP_CodeCoverage object to a file. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_PHP +{ + /** + * @param PHP_CodeCoverage $coverage + * @param string $target + * @return string + */ + public function process(PHP_CodeCoverage $coverage, $target = NULL) + { + $coverage = serialize($coverage); + + if ($target !== NULL) { + return file_put_contents($target, $coverage); + } else { + return $coverage; + } + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Text.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Text.php new file mode 100644 index 0000000..5eecb09 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Text.php @@ -0,0 +1,278 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +/** + * Generates human readable output from an PHP_CodeCoverage object. + * + * The output gets put into a text file our written to the CLI. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_Text +{ + protected $outputStream; + protected $lowUpperBound; + protected $highLowerBound; + protected $showUncoveredFiles; + + protected $colors = array( + 'green' => "\x1b[30;42m", + 'yellow' => "\x1b[30;43m", + 'red' => "\x1b[37;41m", + 'header' => "\x1b[47;40m", + 'reset' => "\x1b[0m", + 'eol' => "\x1b[2K", + ); + + public function __construct(PHPUnit_Util_Printer $outputStream, $lowUpperBound, $highLowerBound, $showUncoveredFiles) + { + $this->outputStream = $outputStream; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->showUncoveredFiles = $showUncoveredFiles; + } + + /** + * @param PHP_CodeCoverage $coverage + * @param string $target + * @param string $name + * @return string + */ + public function process(PHP_CodeCoverage $coverage, $showColors = FALSE) + { + $output = ''; + $report = $coverage->getReport(); + unset($coverage); + + $colors = array( + 'header' => '', + 'classes' => '', + 'methods' => '', + 'lines' => '', + 'reset' => '', + 'eol' => '' + ); + + if ($showColors) { + $colors['classes'] = $this->getCoverageColor( + $report->getNumTestedClassesAndTraits(), + $report->getNumClassesAndTraits() + ); + $colors['methods'] = $this->getCoverageColor( + $report->getNumTestedMethods(), + $report->getNumMethods() + ); + $colors['lines'] = $this->getCoverageColor( + $report->getNumExecutedLines(), + $report->getNumExecutableLines() + ); + $colors['reset'] = $this->colors['reset']; + $colors['header'] = $this->colors['header']; + $colors['eol'] = $this->colors['eol']; + } + + $output .= PHP_EOL . PHP_EOL . + $colors['header'] . 'Code Coverage Report '; + + $output .= PHP_EOL . + date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . + PHP_EOL; + + $output .= PHP_EOL . ' Summary: ' . PHP_EOL . $colors['reset'] + . $colors['classes'] . $colors['eol'] . ' Classes: ' . PHP_CodeCoverage_Util::percent($report->getNumTestedClassesAndTraits(), $report->getNumClassesAndTraits(), TRUE) + . ' (' . $report->getNumTestedClassesAndTraits() . '/' . $report->getNumClassesAndTraits() . ')' . PHP_EOL . $colors ['eol'] + . $colors['methods'] . $colors['eol'] . ' Methods: ' . PHP_CodeCoverage_Util::percent($report->getNumTestedMethods(), $report->getNumMethods(), TRUE) + . ' (' . $report->getNumTestedMethods() . '/' . $report->getNumMethods() . ')' . PHP_EOL . $colors ['eol'] + . $colors['lines'] . $colors['eol'] . ' Lines: ' . PHP_CodeCoverage_Util::percent($report->getNumExecutedLines(), $report->getNumExecutableLines(), TRUE) + . ' (' . $report->getNumExecutedLines() . '/' . $report->getNumExecutableLines() . ')' . PHP_EOL . $colors['reset'] . $colors ['eol']; + + $classCoverage = array(); + + foreach ($report as $item) { + if (!$item instanceof PHP_CodeCoverage_Report_Node_File) { + continue; + } + + $classes = $item->getClassesAndTraits(); + $coverage = $item->getCoverageData(); + $lines = array(); + $ignoredLines = $item->getIgnoredLines(); + + foreach ($classes as $className => $class) { + $classStatements = 0; + $coveredClassStatements = 0; + $coveredMethods = 0; + + foreach ($class['methods'] as $method) { + $methodCount = 0; + $methodLines = 0; + $methodLinesCovered = 0; + + for ($i = $method['startLine']; + $i <= $method['endLine']; + $i++) { + if (isset($ignoredLines[$i])) { + continue; + } + + $add = TRUE; + $count = 0; + + if (isset($coverage[$i])) { + if ($coverage[$i] !== NULL) { + $classStatements++; + $methodLines++; + } else { + $add = FALSE; + } + + $count = count($coverage[$i]); + + if ($count > 0) { + $coveredClassStatements++; + $methodLinesCovered++; + } + } else { + $add = FALSE; + } + + $methodCount = max($methodCount, $count); + + if ($add) { + $lines[$i] = array( + 'count' => $count, 'type' => 'stmt' + ); + } + } + + if ($methodCount > 0) { + $coveredMethods++; + } + + } + + if (!empty($class['package']['namespace'])) { + $namespace = '\\' . $class['package']['namespace'] . '::'; + } + + else if (!empty($class['package']['fullPackage'])) { + $namespace = '@' . $class['package']['fullPackage'] . '::'; + } + + else { + $namespace = ''; + } + + $classCoverage[$namespace . $className] = array( + 'namespace' => $namespace, + 'className ' => $className, + 'methodsCovered' => $coveredMethods, + 'methodCount' => count($class['methods']), + 'statementsCovered' => $coveredClassStatements, + 'statementCount' => $classStatements, + ); + } + } + + ksort($classCoverage); + + $methodColor = ''; + $linesColor = ''; + $resetColor = ''; + + foreach ($classCoverage as $fullQualifiedPath => $classInfo) { + if ($classInfo['statementsCovered'] != 0 || + $this->showUncoveredFiles) { + + if ($showColors) { + $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); + $linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); + $resetColor = $colors['reset']; + } + + $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL + . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ' + . ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor + ; + } + } + + $this->outputStream->write($output . PHP_EOL); + } + + protected function getCoverageColor($numberOfCoveredElements, $totalNumberOfElements) + { + $coverage = PHP_CodeCoverage_Util::percent( + $numberOfCoveredElements, $totalNumberOfElements + ); + + if ($coverage > $this->highLowerBound) { + return $this->colors['green']; + } + + else if ($coverage > $this->lowUpperBound) { + return $this->colors['yellow']; + } + + return $this->colors['red']; + } + + protected function printCoverageCounts($numberOfCoveredElements, $totalNumberOfElements, $presicion) + { + $format = '%' . $presicion . 's'; + + return PHP_CodeCoverage_Util::percent( + $numberOfCoveredElements, $totalNumberOfElements, TRUE, TRUE + ) . + ' (' . sprintf($format, $numberOfCoveredElements) . '/' . + sprintf($format, $totalNumberOfElements) . ')'; + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util.php new file mode 100644 index 0000000..823ea82 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util.php @@ -0,0 +1,268 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Utility methods. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_Util +{ + /** + * @var array + */ + protected static $ignoredLines = array(); + + /** + * @var array + */ + protected static $ids = array(); + + + /** + * Returns the lines of a source file that should be ignored. + * + * @param string $filename + * @param boolean $cacheTokens + * @return array + * @throws PHP_CodeCoverage_Exception + */ + public static function getLinesToBeIgnored($filename, $cacheTokens = TRUE) + { + if (!is_string($filename)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'string' + ); + } + + if (!is_bool($cacheTokens)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 2, 'boolean' + ); + } + + if (!isset(self::$ignoredLines[$filename])) { + self::$ignoredLines[$filename] = array(); + $ignore = FALSE; + $stop = FALSE; + $lines = file($filename); + + foreach ($lines as $index => $line) { + if (!trim($line)) { + self::$ignoredLines[$filename][$index+1] = TRUE; + } + } + + if ($cacheTokens) { + $tokens = PHP_Token_Stream_CachingFactory::get($filename); + } else { + $tokens = new PHP_Token_Stream($filename); + } + + $classes = array_merge($tokens->getClasses(), $tokens->getTraits()); + $tokens = $tokens->tokens(); + + foreach ($tokens as $token) { + switch (get_class($token)) { + case 'PHP_Token_COMMENT': + case 'PHP_Token_DOC_COMMENT': { + $count = substr_count($token, "\n"); + $line = $token->getLine(); + + for ($i = $line; $i < $line + $count; $i++) { + self::$ignoredLines[$filename][$i] = TRUE; + } + + if ($token instanceof PHP_Token_DOC_COMMENT) { + // Workaround for the fact the DOC_COMMENT token + // does not include the final \n character in its + // text. + if (substr(trim($lines[$i-1]), -2) == '*/') { + self::$ignoredLines[$filename][$i] = TRUE; + } + + break; + } + + $_token = trim($token); + + if ($_token == '// @codeCoverageIgnore' || + $_token == '//@codeCoverageIgnore') { + $ignore = TRUE; + $stop = TRUE; + } + + else if ($_token == '// @codeCoverageIgnoreStart' || + $_token == '//@codeCoverageIgnoreStart') { + $ignore = TRUE; + } + + else if ($_token == '// @codeCoverageIgnoreEnd' || + $_token == '//@codeCoverageIgnoreEnd') { + $stop = TRUE; + } + } + break; + + case 'PHP_Token_INTERFACE': + case 'PHP_Token_TRAIT': + case 'PHP_Token_CLASS': + case 'PHP_Token_FUNCTION': { + $docblock = $token->getDocblock(); + + if (strpos($docblock, '@codeCoverageIgnore')) { + $endLine = $token->getEndLine(); + + for ($i = $token->getLine(); $i <= $endLine; $i++) { + self::$ignoredLines[$filename][$i] = TRUE; + } + } + + else if ($token instanceof PHP_Token_INTERFACE || + $token instanceof PHP_Token_TRAIT || + $token instanceof PHP_Token_CLASS) { + if (empty($classes[$token->getName()]['methods'])) { + for ($i = $token->getLine(); + $i <= $token->getEndLine(); + $i++) { + self::$ignoredLines[$filename][$i] = TRUE; + } + } else { + $firstMethod = array_shift( + $classes[$token->getName()]['methods'] + ); + + $lastMethod = array_pop( + $classes[$token->getName()]['methods'] + ); + + if ($lastMethod === NULL) { + $lastMethod = $firstMethod; + } + + for ($i = $token->getLine(); + $i < $firstMethod['startLine']; + $i++) { + self::$ignoredLines[$filename][$i] = TRUE; + } + + for ($i = $token->getEndLine(); + $i > $lastMethod['endLine']; + $i--) { + self::$ignoredLines[$filename][$i] = TRUE; + } + } + } + } + break; + + case 'PHP_Token_INTERFACE': { + $endLine = $token->getEndLine(); + + for ($i = $token->getLine(); $i <= $endLine; $i++) { + self::$ignoredLines[$filename][$i] = TRUE; + } + } + break; + + case 'PHP_Token_NAMESPACE': { + self::$ignoredLines[$filename][$token->getEndLine()] = TRUE; + } // Intentional fallthrough + case 'PHP_Token_OPEN_TAG': + case 'PHP_Token_CLOSE_TAG': + case 'PHP_Token_USE': { + self::$ignoredLines[$filename][$token->getLine()] = TRUE; + } + break; + } + + if ($ignore) { + self::$ignoredLines[$filename][$token->getLine()] = TRUE; + + if ($stop) { + $ignore = FALSE; + $stop = FALSE; + } + } + } + } + + return self::$ignoredLines[$filename]; + } + + /** + * @param float $a + * @param float $b + * @return float ($a / $b) * 100 + */ + public static function percent($a, $b, $asString = FALSE, $fixedWidth = FALSE) + { + if ($asString && $b == 0) { + return ''; + } + + if ($b > 0) { + $percent = ($a / $b) * 100; + } else { + $percent = 100; + } + + if ($asString) { + if ($fixedWidth) { + return sprintf('%6.2F%%', $percent); + } + + return sprintf('%01.2F%%', $percent); + } else { + return $percent; + } + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util/InvalidArgumentHelper.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util/InvalidArgumentHelper.php new file mode 100644 index 0000000..cd80a58 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Util/InvalidArgumentHelper.php @@ -0,0 +1,80 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.2.0 + */ + +/** + * Factory for PHP_CodeCoverage_Exception objects that are used to describe + * invalid arguments passed to a function or method. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.2.0 + */ +class PHP_CodeCoverage_Util_InvalidArgumentHelper +{ + /** + * @param integer $argument + * @param string $type + * @param mixed $value + */ + public static function factory($argument, $type, $value = NULL) + { + $stack = debug_backtrace(FALSE); + + return new PHP_CodeCoverage_Exception( + sprintf( + 'Argument #%d%sof %s::%s() must be a %s', + $argument, + $value !== NULL ? ' (' . $value . ')' : ' ', + $stack[1]['class'], + $stack[1]['function'], + $type + ) + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Version.php b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Version.php new file mode 100644 index 0000000..19f9869 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Version.php @@ -0,0 +1,92 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.2.1 + */ + +/** + * + * + * @category PHP + * @package CodeCoverage + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.2.1 + */ +class PHP_CodeCoverage_Version +{ + const VERSION = '1.2.6'; + protected static $version; + + /** + * Returns the version of PHP_CodeCoverage. + * + * @return string + */ + public static function id() + { + if (self::$version === NULL) { + self::$version = self::VERSION; + + if (is_dir(dirname(dirname(__DIR__)) . '/.git')) { + $dir = getcwd(); + chdir(__DIR__); + $version = exec('git describe --tags'); + chdir($dir); + + if ($version) { + if (count(explode('.', self::VERSION)) == 3) { + self::$version = $version; + } else { + $version = explode('-', $version); + + self::$version = self::VERSION . '-' . $version[2]; + } + } + } + } + + return self::$version; + } +} diff --git a/vendor/phpunit/php-code-coverage/README.markdown b/vendor/phpunit/php-code-coverage/README.markdown new file mode 100644 index 0000000..559afa7 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/README.markdown @@ -0,0 +1,43 @@ +PHP_CodeCoverage +================ + +**PHP_CodeCoverage** is a library that provides collection, processing, and rendering functionality for PHP code coverage information. + +Requirements +------------ + +* PHP_CodeCoverage 1.2 requires PHP 5.3.3 (or later) but PHP 5.4.7 (or later) is highly recommended. +* [Xdebug](http://xdebug.org/) 2.0.5 (or later) is required but Xdebug 2.2.1 (or later) is highly recommended. + +Installation +------------ + +PHP_CodeCoverage should be installed using the PEAR Installer, the backbone of the [PHP Extension and Application Repository](http://pear.php.net/) that provides a distribution system for PHP packages. + +Depending on your OS distribution and/or your PHP environment, you may need to install PEAR or update your existing PEAR installation before you can proceed with the following instructions. `sudo pear upgrade PEAR` usually suffices to upgrade an existing PEAR installation. The [PEAR Manual ](http://pear.php.net/manual/en/installation.getting.php) explains how to perform a fresh installation of PEAR. + +The following two commands (which you may have to run as `root`) are all that is required to install PHP_CodeCoverage using the PEAR Installer: + + pear config-set auto_discover 1 + pear install pear.phpunit.de/PHP_CodeCoverage + +After the installation you can find the PHP_CodeCoverage source files inside your local PEAR directory; the path is usually `/usr/lib/php/PHP/CodeCoverage`. + +Using the PHP_CodeCoverage API +------------------------------ + + start(''); + + // ... + + $coverage->stop(); + + $writer = new PHP_CodeCoverage_Report_Clover; + $writer->process($coverage, '/tmp/clover.xml'); + + $writer = new PHP_CodeCoverage_Report_HTML; + $writer->process($coverage, '/tmp/code-coverage-report'); diff --git a/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/FilterTest.php b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/FilterTest.php new file mode 100644 index 0000000..cac28a2 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/FilterTest.php @@ -0,0 +1,298 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +/** + * Tests for the PHP_CodeCoverage_Filter class. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_FilterTest extends PHPUnit_Framework_TestCase +{ + protected $filter; + protected $files; + + protected function setUp() + { + $this->filter = unserialize('O:23:"PHP_CodeCoverage_Filter":0:{}'); + + $this->files = array( + TEST_FILES_PATH . 'BankAccount.php', + TEST_FILES_PATH . 'BankAccountTest.php', + TEST_FILES_PATH . 'CoverageClassExtendedTest.php', + TEST_FILES_PATH . 'CoverageClassTest.php', + TEST_FILES_PATH . 'CoverageFunctionTest.php', + TEST_FILES_PATH . 'CoverageMethodOneLineAnnotationTest.php', + TEST_FILES_PATH . 'CoverageMethodTest.php', + TEST_FILES_PATH . 'CoverageNoneTest.php', + TEST_FILES_PATH . 'CoverageNotPrivateTest.php', + TEST_FILES_PATH . 'CoverageNotProtectedTest.php', + TEST_FILES_PATH . 'CoverageNotPublicTest.php', + TEST_FILES_PATH . 'CoverageNothingTest.php', + TEST_FILES_PATH . 'CoveragePrivateTest.php', + TEST_FILES_PATH . 'CoverageProtectedTest.php', + TEST_FILES_PATH . 'CoveragePublicTest.php', + TEST_FILES_PATH . 'CoverageTwoDefaultClassAnnotations.php', + TEST_FILES_PATH . 'CoveredClass.php', + TEST_FILES_PATH . 'CoveredFunction.php', + TEST_FILES_PATH . 'NamespaceCoverageClassExtendedTest.php', + TEST_FILES_PATH . 'NamespaceCoverageClassTest.php', + TEST_FILES_PATH . 'NamespaceCoverageCoversClassPublicTest.php', + TEST_FILES_PATH . 'NamespaceCoverageCoversClassTest.php', + TEST_FILES_PATH . 'NamespaceCoverageMethodTest.php', + TEST_FILES_PATH . 'NamespaceCoverageNotPrivateTest.php', + TEST_FILES_PATH . 'NamespaceCoverageNotProtectedTest.php', + TEST_FILES_PATH . 'NamespaceCoverageNotPublicTest.php', + TEST_FILES_PATH . 'NamespaceCoveragePrivateTest.php', + TEST_FILES_PATH . 'NamespaceCoverageProtectedTest.php', + TEST_FILES_PATH . 'NamespaceCoveragePublicTest.php', + TEST_FILES_PATH . 'NamespaceCoveredClass.php', + TEST_FILES_PATH . 'NotExistingCoveredElementTest.php', + TEST_FILES_PATH . 'source_with_ignore.php', + TEST_FILES_PATH . 'source_with_namespace.php', + TEST_FILES_PATH . 'source_with_oneline_annotations.php', + TEST_FILES_PATH . 'source_without_ignore.php', + TEST_FILES_PATH . 'source_without_namespace.php' + ); + } + + /** + * @covers PHP_CodeCoverage_Filter::addFileToBlacklist + * @covers PHP_CodeCoverage_Filter::getBlacklist + */ + public function testAddingAFileToTheBlacklistWorks() + { + $this->filter->addFileToBlacklist($this->files[0]); + + $this->assertEquals( + array($this->files[0]), $this->filter->getBlacklist() + ); + } + + /** + * @covers PHP_CodeCoverage_Filter::removeFileFromBlacklist + * @covers PHP_CodeCoverage_Filter::getBlacklist + */ + public function testRemovingAFileFromTheBlacklistWorks() + { + $this->filter->addFileToBlacklist($this->files[0]); + $this->filter->removeFileFromBlacklist($this->files[0]); + + $this->assertEquals(array(), $this->filter->getBlacklist()); + } + + /** + * @covers PHP_CodeCoverage_Filter::addDirectoryToBlacklist + * @covers PHP_CodeCoverage_Filter::getBlacklist + * @depends testAddingAFileToTheBlacklistWorks + */ + public function testAddingADirectoryToTheBlacklistWorks() + { + $this->filter->addDirectoryToBlacklist(TEST_FILES_PATH); + + $blacklist = $this->filter->getBlacklist(); + sort($blacklist); + + $this->assertEquals($this->files, $blacklist); + } + + /** + * @covers PHP_CodeCoverage_Filter::addFilesToBlacklist + * @covers PHP_CodeCoverage_Filter::getBlacklist + */ + public function testAddingFilesToTheBlacklistWorks() + { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + TEST_FILES_PATH, $suffixes = '.php' + ); + + $this->filter->addFilesToBlacklist($files); + + $blacklist = $this->filter->getBlacklist(); + sort($blacklist); + + $this->assertEquals($this->files, $blacklist); + } + + /** + * @covers PHP_CodeCoverage_Filter::removeDirectoryFromBlacklist + * @covers PHP_CodeCoverage_Filter::getBlacklist + * @depends testAddingADirectoryToTheBlacklistWorks + */ + public function testRemovingADirectoryFromTheBlacklistWorks() + { + $this->filter->addDirectoryToBlacklist(TEST_FILES_PATH); + $this->filter->removeDirectoryFromBlacklist(TEST_FILES_PATH); + + $this->assertEquals(array(), $this->filter->getBlacklist()); + } + + /** + * @covers PHP_CodeCoverage_Filter::addFileToWhitelist + * @covers PHP_CodeCoverage_Filter::getWhitelist + */ + public function testAddingAFileToTheWhitelistWorks() + { + $this->filter->addFileToWhitelist($this->files[0]); + + $this->assertEquals( + array($this->files[0]), $this->filter->getWhitelist() + ); + } + + /** + * @covers PHP_CodeCoverage_Filter::removeFileFromWhitelist + * @covers PHP_CodeCoverage_Filter::getWhitelist + */ + public function testRemovingAFileFromTheWhitelistWorks() + { + $this->filter->addFileToWhitelist($this->files[0]); + $this->filter->removeFileFromWhitelist($this->files[0]); + + $this->assertEquals(array(), $this->filter->getWhitelist()); + } + + /** + * @covers PHP_CodeCoverage_Filter::addDirectoryToWhitelist + * @covers PHP_CodeCoverage_Filter::getWhitelist + * @depends testAddingAFileToTheWhitelistWorks + */ + public function testAddingADirectoryToTheWhitelistWorks() + { + $this->filter->addDirectoryToWhitelist(TEST_FILES_PATH); + + $whitelist = $this->filter->getWhitelist(); + sort($whitelist); + + $this->assertEquals($this->files, $whitelist); + } + + /** + * @covers PHP_CodeCoverage_Filter::addFilesToWhitelist + * @covers PHP_CodeCoverage_Filter::getBlacklist + */ + public function testAddingFilesToTheWhitelistWorks() + { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + TEST_FILES_PATH, $suffixes = '.php' + ); + + $this->filter->addFilesToWhitelist($files); + + $whitelist = $this->filter->getWhitelist(); + sort($whitelist); + + $this->assertEquals($this->files, $whitelist); + } + + /** + * @covers PHP_CodeCoverage_Filter::removeDirectoryFromWhitelist + * @covers PHP_CodeCoverage_Filter::getWhitelist + * @depends testAddingADirectoryToTheWhitelistWorks + */ + public function testRemovingADirectoryFromTheWhitelistWorks() + { + $this->filter->addDirectoryToWhitelist(TEST_FILES_PATH); + $this->filter->removeDirectoryFromWhitelist(TEST_FILES_PATH); + + $this->assertEquals(array(), $this->filter->getWhitelist()); + } + + /** + * @covers PHP_CodeCoverage_Filter::isFile + */ + public function testIsFile() + { + $this->assertFalse($this->filter->isFile('eval()\'d code')); + $this->assertFalse($this->filter->isFile('runtime-created function')); + $this->assertFalse($this->filter->isFile('assert code')); + $this->assertFalse($this->filter->isFile('regexp code')); + $this->assertTrue($this->filter->isFile('filename')); + } + + /** + * @covers PHP_CodeCoverage_Filter::isFiltered + */ + public function testBlacklistedFileIsFiltered() + { + $this->filter->addFileToBlacklist($this->files[0]); + $this->assertTrue($this->filter->isFiltered($this->files[0])); + } + + /** + * @covers PHP_CodeCoverage_Filter::isFiltered + */ + public function testWhitelistedFileIsNotFiltered() + { + $this->filter->addFileToWhitelist($this->files[0]); + $this->assertFalse($this->filter->isFiltered($this->files[0])); + } + + /** + * @covers PHP_CodeCoverage_Filter::isFiltered + */ + public function testNotWhitelistedFileIsFiltered() + { + $this->filter->addFileToWhitelist($this->files[0]); + $this->assertTrue($this->filter->isFiltered($this->files[1])); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/CloverTest.php b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/CloverTest.php new file mode 100644 index 0000000..ac25594 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/CloverTest.php @@ -0,0 +1,96 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once TEST_FILES_PATH . '../TestCase.php'; + +/** + * Tests for the PHP_CodeCoverage_Report_Clover class. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_Report_CloverTest extends PHP_CodeCoverage_TestCase +{ + /** + * @covers PHP_CodeCoverage_Report_Clover + */ + public function testCloverForBankAccountTest() + { + $clover = new PHP_CodeCoverage_Report_Clover; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'BankAccount-clover.xml', + $clover->process($this->getCoverageForBankAccount(), NULL, 'BankAccount') + ); + } + + /** + * @covers PHP_CodeCoverage_Report_Clover + */ + public function testCloverForFileWithIgnoredLines() + { + $clover = new PHP_CodeCoverage_Report_Clover; + + $this->assertStringMatchesFormatFile( + TEST_FILES_PATH . 'ignored-lines-clover.xml', + $clover->process($this->getCoverageForFileWithIgnoredLines()) + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/FactoryTest.php b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/FactoryTest.php new file mode 100644 index 0000000..64db752 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/Report/FactoryTest.php @@ -0,0 +1,263 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.1.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(dirname(dirname(__FILE__)))) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once TEST_FILES_PATH . '../TestCase.php'; + +/** + * Tests for the PHP_CodeCoverage_Report_Factory class. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.1.0 + */ +class PHP_CodeCoverage_Report_FactoryTest extends PHP_CodeCoverage_TestCase +{ + protected $factory; + + protected function setUp() + { + $this->factory = new PHP_CodeCoverage_Report_Factory; + } + + public function testSomething() + { + $root = $this->getCoverageForBankAccount()->getReport(); + + $expectedPath = rtrim(TEST_FILES_PATH, DIRECTORY_SEPARATOR); + $this->assertEquals($expectedPath, $root->getName()); + $this->assertEquals($expectedPath, $root->getPath()); + $this->assertEquals(10, $root->getNumExecutableLines()); + $this->assertEquals(5, $root->getNumExecutedLines()); + $this->assertEquals(1, $root->getNumClasses()); + $this->assertEquals(0, $root->getNumTestedClasses()); + $this->assertEquals(4, $root->getNumMethods()); + $this->assertEquals(3, $root->getNumTestedMethods()); + $this->assertEquals('0.00%', $root->getTestedClassesPercent()); + $this->assertEquals('75.00%', $root->getTestedMethodsPercent()); + $this->assertEquals('50.00%', $root->getLineExecutedPercent()); + $this->assertEquals(0, $root->getNumFunctions()); + $this->assertEquals(0, $root->getNumTestedFunctions()); + $this->assertNull($root->getParent()); + $this->assertEquals(array(), $root->getDirectories()); + #$this->assertEquals(array(), $root->getFiles()); + #$this->assertEquals(array(), $root->getChildNodes()); + + $this->assertEquals( + array( + 'BankAccount' => array( + 'methods' => array( + 'getBalance' => array( + 'signature' => 'getBalance()', + 'startLine' => 6, + 'endLine' => 9, + 'executableLines' => 1, + 'executedLines' => 1, + 'ccn' => 1, + 'coverage' => 100, + 'crap' => '1', + 'link' => 'BankAccount.php.html#6', + 'methodName' => 'getBalance' + ), + 'setBalance' => array( + 'signature' => 'setBalance($balance)', + 'startLine' => 11, + 'endLine' => 18, + 'executableLines' => 5, + 'executedLines' => 0, + 'ccn' => 2, + 'coverage' => 0, + 'crap' => 6, + 'link' => 'BankAccount.php.html#11', + 'methodName' => 'setBalance' + ), + 'depositMoney' => array( + 'signature' => 'depositMoney($balance)', + 'startLine' => 20, + 'endLine' => 25, + 'executableLines' => 2, + 'executedLines' => 2, + 'ccn' => 1, + 'coverage' => 100, + 'crap' => '1', + 'link' => 'BankAccount.php.html#20', + 'methodName' => 'depositMoney' + ), + 'withdrawMoney' => array( + 'signature' => 'withdrawMoney($balance)', + 'startLine' => 27, + 'endLine' => 32, + 'executableLines' => 2, + 'executedLines' => 2, + 'ccn' => 1, + 'coverage' => 100, + 'crap' => '1', + 'link' => 'BankAccount.php.html#27', + 'methodName' => 'withdrawMoney' + ), + ), + 'startLine' => 2, + 'executableLines' => 10, + 'executedLines' => 5, + 'ccn' => 5, + 'coverage' => 50, + 'crap' => '8.12', + 'package' => array( + 'namespace' => '', + 'fullPackage' => '', + 'category' => '', + 'package' => '', + 'subpackage' => '' + ), + 'link' => 'BankAccount.php.html#2', + 'className' => 'BankAccount' + ) + ), + $root->getClasses() + ); + + $this->assertEquals(array(), $root->getFunctions()); + } + + /** + * @covers PHP_CodeCoverage_Report_Factory::buildDirectoryStructure + */ + public function testBuildDirectoryStructure() + { + $method = new ReflectionMethod( + 'PHP_CodeCoverage_Report_Factory', 'buildDirectoryStructure' + ); + + $method->setAccessible(TRUE); + + $this->assertEquals( + array( + 'src' => array( + 'Money.php/f' => array(), + 'MoneyBag.php/f' => array() + ) + ), + $method->invoke( + $this->factory, + array('src/Money.php' => array(), 'src/MoneyBag.php' => array()) + ) + ); + } + + /** + * @covers PHP_CodeCoverage_Report_Factory::reducePaths + * @dataProvider reducePathsProvider + */ + public function testReducePaths($reducedPaths, $commonPath, $paths) + { + $method = new ReflectionMethod( + 'PHP_CodeCoverage_Report_Factory', 'reducePaths' + ); + + $method->setAccessible(TRUE); + + $_commonPath = $method->invokeArgs($this->factory, array(&$paths)); + + $this->assertEquals($reducedPaths, $paths); + $this->assertEquals($commonPath, $_commonPath); + } + + public function reducePathsProvider() + { + return array( + array( + array( + 'Money.php' => array(), + 'MoneyBag.php' => array() + ), + '/home/sb/Money', + array( + '/home/sb/Money/Money.php' => array(), + '/home/sb/Money/MoneyBag.php' => array() + ) + ), + array( + array( + 'Money.php' => array() + ), + '/home/sb/Money/', + array( + '/home/sb/Money/Money.php' => array() + ) + ), + array( + array(), + '.', + array() + ), + array( + array( + 'Money.php' => array(), + 'MoneyBag.php' => array(), + 'Cash.phar/Cash.php' => array(), + ), + '/home/sb/Money', + array( + '/home/sb/Money/Money.php' => array(), + '/home/sb/Money/MoneyBag.php' => array(), + 'phar:///home/sb/Money/Cash.phar/Cash.php' => array(), + ), + ), + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/UtilTest.php b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/UtilTest.php new file mode 100644 index 0000000..75e8473 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverage/UtilTest.php @@ -0,0 +1,194 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once TEST_FILES_PATH . 'CoverageClassExtendedTest.php'; +require_once TEST_FILES_PATH . 'CoverageClassTest.php'; +require_once TEST_FILES_PATH . 'CoverageFunctionTest.php'; +require_once TEST_FILES_PATH . 'CoverageMethodTest.php'; +require_once TEST_FILES_PATH . 'CoverageMethodOneLineAnnotationTest.php'; +require_once TEST_FILES_PATH . 'CoverageNoneTest.php'; +require_once TEST_FILES_PATH . 'CoverageNotPrivateTest.php'; +require_once TEST_FILES_PATH . 'CoverageNotProtectedTest.php'; +require_once TEST_FILES_PATH . 'CoverageNotPublicTest.php'; +require_once TEST_FILES_PATH . 'CoveragePrivateTest.php'; +require_once TEST_FILES_PATH . 'CoverageProtectedTest.php'; +require_once TEST_FILES_PATH . 'CoveragePublicTest.php'; +require_once TEST_FILES_PATH . 'CoverageTwoDefaultClassAnnotations.php'; +require_once TEST_FILES_PATH . 'CoveredClass.php'; +require_once TEST_FILES_PATH . 'CoveredFunction.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageClassExtendedTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageClassTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageCoversClassTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageCoversClassPublicTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageMethodTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageNotPrivateTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageNotProtectedTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageNotPublicTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoveragePrivateTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoverageProtectedTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoveragePublicTest.php'; +require_once TEST_FILES_PATH . 'NamespaceCoveredClass.php'; +require_once TEST_FILES_PATH . 'NotExistingCoveredElementTest.php'; +require_once TEST_FILES_PATH . 'CoverageNothingTest.php'; +/** + * Tests for the PHP_CodeCoverage_Util class. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverage_UtilTest extends PHPUnit_Framework_TestCase +{ + /** + * @covers PHP_CodeCoverage_Util::getLinesToBeIgnored + */ + public function testGetLinesToBeIgnored() + { + $this->assertEquals( + array( + 1 => TRUE, + 3 => TRUE, + 4 => TRUE, + 5 => TRUE, + 7 => TRUE, + 8 => TRUE, + 9 => TRUE, + 10 => TRUE, + 11 => TRUE, + 12 => TRUE, + 13 => TRUE, + 14 => TRUE, + 15 => TRUE, + 16 => TRUE, + 17 => TRUE, + 18 => TRUE, + 19 => TRUE, + 20 => TRUE, + 21 => TRUE, + 22 => TRUE, + 23 => TRUE, + 24 => TRUE, + 25 => TRUE, + 26 => TRUE, + 27 => TRUE, + 30 => TRUE, + 32 => TRUE, + 33 => TRUE, + 34 => TRUE, + 35 => TRUE, + 36 => TRUE, + 37 => TRUE, + 38 => TRUE, + ), + PHP_CodeCoverage_Util::getLinesToBeIgnored( + TEST_FILES_PATH . 'source_with_ignore.php' + ) + ); + } + + /** + * @covers PHP_CodeCoverage_Util::getLinesToBeIgnored + */ + public function testGetLinesToBeIgnored2() + { + $this->assertEquals( + array(1 => TRUE), + PHP_CodeCoverage_Util::getLinesToBeIgnored( + TEST_FILES_PATH . 'source_without_ignore.php' + ) + ); + } + + /** + * @covers PHP_CodeCoverage_Util::getLinesToBeIgnored + */ + public function testGetLinesToBeIgnoredOneLineAnnotations() + { + $this->assertEquals( + array( + 1 => TRUE, + 2 => TRUE, + 7 => TRUE, + 3 => TRUE, + 4 => TRUE, + 5 => TRUE, + 6 => TRUE, + 8 => TRUE, + 9 => TRUE, + 13 => TRUE, + ), + PHP_CodeCoverage_Util::getLinesToBeIgnored( + TEST_FILES_PATH . 'source_with_oneline_annotations.php' + ) + ); + } + + + /** + * @covers PHP_CodeCoverage_Util::percent + */ + public function testPercent() + { + $this->assertEquals(100, PHP_CodeCoverage_Util::percent(100, 0)); + $this->assertEquals(100, PHP_CodeCoverage_Util::percent(100, 100)); + $this->assertEquals( + '100.00%', PHP_CodeCoverage_Util::percent(100, 100, TRUE) + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverageTest.php b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverageTest.php new file mode 100644 index 0000000..7d0e0ce --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/PHP/CodeCoverageTest.php @@ -0,0 +1,499 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once TEST_FILES_PATH . '../TestCase.php'; + +require_once TEST_FILES_PATH . 'BankAccount.php'; +require_once TEST_FILES_PATH . 'BankAccountTest.php'; + +/** + * Tests for the PHP_CodeCoverage class. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +class PHP_CodeCoverageTest extends PHP_CodeCoverage_TestCase +{ + protected $coverage; + protected $getLinesToBeCovered; + + protected function setUp() + { + $this->coverage = new PHP_CodeCoverage; + + $this->getLinesToBeCovered = new ReflectionMethod( + 'PHP_CodeCoverage', 'getLinesToBeCovered' + ); + + $this->getLinesToBeCovered->setAccessible(TRUE); + } + + /** + * @covers PHP_CodeCoverage::__construct + * @covers PHP_CodeCoverage::filter + */ + public function testConstructor() + { + $this->assertAttributeInstanceOf( + 'PHP_CodeCoverage_Driver_Xdebug', 'driver', $this->coverage + ); + + $this->assertAttributeInstanceOf( + 'PHP_CodeCoverage_Filter', 'filter', $this->coverage + ); + } + + /** + * @covers PHP_CodeCoverage::__construct + * @covers PHP_CodeCoverage::filter + */ + public function testConstructor2() + { + $filter = new PHP_CodeCoverage_Filter; + $coverage = new PHP_CodeCoverage(NULL, $filter); + + $this->assertAttributeInstanceOf( + 'PHP_CodeCoverage_Driver_Xdebug', 'driver', $coverage + ); + + $this->assertSame($filter, $coverage->filter()); + } + + /** + * @covers PHP_CodeCoverage::start + * @expectedException PHP_CodeCoverage_Exception + */ + public function testStartThrowsExceptionForInvalidArgument() + { + $this->coverage->start(NULL, array(), NULL); + } + + /** + * @covers PHP_CodeCoverage::stop + * @expectedException PHP_CodeCoverage_Exception + */ + public function testStopThrowsExceptionForInvalidArgument() + { + $this->coverage->stop(NULL); + } + + /** + * @covers PHP_CodeCoverage::append + * @expectedException PHP_CodeCoverage_Exception + */ + public function testAppendThrowsExceptionForInvalidArgument() + { + $this->coverage->append(array(), NULL); + } + + /** + * @covers PHP_CodeCoverage::setCacheTokens + * @expectedException PHP_CodeCoverage_Exception + */ + public function testSetCacheTokensThrowsExceptionForInvalidArgument() + { + $this->coverage->setCacheTokens(NULL); + } + + /** + * @covers PHP_CodeCoverage::setCacheTokens + */ + public function testSetCacheTokens() + { + $this->coverage->setCacheTokens(TRUE); + $this->assertAttributeEquals(TRUE, 'cacheTokens', $this->coverage); + } + + /** + * @covers PHP_CodeCoverage::setForceCoversAnnotation + * @expectedException PHP_CodeCoverage_Exception + */ + public function testSetForceCoversAnnotationThrowsExceptionForInvalidArgument() + { + $this->coverage->setForceCoversAnnotation(NULL); + } + + /** + * @covers PHP_CodeCoverage::setForceCoversAnnotation + */ + public function testSetForceCoversAnnotation() + { + $this->coverage->setForceCoversAnnotation(TRUE); + $this->assertAttributeEquals( + TRUE, 'forceCoversAnnotation', $this->coverage + ); + } + + /** + * @covers PHP_CodeCoverage::setProcessUncoveredFilesFromWhitelist + * @expectedException PHP_CodeCoverage_Exception + */ + public function testSetProcessUncoveredFilesFromWhitelistThrowsExceptionForInvalidArgument() + { + $this->coverage->setProcessUncoveredFilesFromWhitelist(NULL); + } + + /** + * @covers PHP_CodeCoverage::setProcessUncoveredFilesFromWhitelist + */ + public function testSetProcessUncoveredFilesFromWhitelist() + { + $this->coverage->setProcessUncoveredFilesFromWhitelist(TRUE); + $this->assertAttributeEquals( + TRUE, 'processUncoveredFilesFromWhitelist', $this->coverage + ); + } + + /** + * @covers PHP_CodeCoverage::setMapTestClassNameToCoveredClassName + */ + public function testSetMapTestClassNameToCoveredClassName() + { + $this->coverage->setMapTestClassNameToCoveredClassName(TRUE); + $this->assertAttributeEquals( + TRUE, 'mapTestClassNameToCoveredClassName', $this->coverage + ); + } + + /** + * @covers PHP_CodeCoverage::setMapTestClassNameToCoveredClassName + * @expectedException PHP_CodeCoverage_Exception + */ + public function testSetMapTestClassNameToCoveredClassNameThrowsExceptionForInvalidArgument() + { + $this->coverage->setMapTestClassNameToCoveredClassName(NULL); + } + + /** + * @covers PHP_CodeCoverage::clear + */ + public function testClear() + { + $this->coverage->clear(); + + $this->assertAttributeEquals(NULL, 'currentId', $this->coverage); + $this->assertAttributeEquals(array(), 'data', $this->coverage); + $this->assertAttributeEquals(array(), 'tests', $this->coverage); + } + + /** + * Add parenthesis to the covers annotation below in a couple of different ways to make sure it + * works as expected + * + * @covers PHP_CodeCoverage::start() + * @covers PHP_CodeCoverage::stop( ) + * @covers PHP_CodeCoverage::append () + * @covers PHP_CodeCoverage::applyListsFilter ( ) + * @covers PHP_CodeCoverage::initializeFilesThatAreSeenTheFirstTime + * @covers PHP_CodeCoverage::applyCoversAnnotationFilter + * @covers PHP_CodeCoverage::getTests + */ + public function testCollect() + { + $coverage = $this->getCoverageForBankAccount(); + + $this->assertEquals( + $this->getExpectedDataArrayForBankAccount(), $coverage->getData() + ); + + $this->assertEquals( + array( + 'BankAccountTest::testBalanceIsInitiallyZero' => NULL, + 'BankAccountTest::testBalanceCannotBecomeNegative' => NULL, + 'BankAccountTest::testBalanceCannotBecomeNegative2' => NULL, + 'BankAccountTest::testDepositWithdrawMoney' => NULL + ), + $coverage->getTests() + ); + } + + /** + * @covers PHP_CodeCoverage::getData + * @covers PHP_CodeCoverage::merge + */ + public function testMerge() + { + $coverage = $this->getCoverageForBankAccountForFirstTwoTests(); + $coverage->merge($this->getCoverageForBankAccountForLastTwoTests()); + + $this->assertEquals( + $this->getExpectedDataArrayForBankAccount(), $coverage->getData() + ); + } + + /** + * @covers PHP_CodeCoverage::getData + * @covers PHP_CodeCoverage::merge + */ + public function testMerge2() + { + $coverage = new PHP_CodeCoverage( + $this->getMock('PHP_CodeCoverage_Driver_Xdebug'), + new PHP_CodeCoverage_Filter + ); + + $coverage->merge($this->getCoverageForBankAccount()); + + $this->assertEquals( + $this->getExpectedDataArrayForBankAccount(), $coverage->getData() + ); + } + + /** + * @covers PHP_CodeCoverage::getLinesToBeCovered + * @covers PHP_CodeCoverage::resolveCoversToReflectionObjects + * @dataProvider getLinesToBeCoveredProvider + */ + public function testGetLinesToBeCovered($test, $lines) + { + if (strpos($test, 'Namespace') === 0) { + $expected = array( + TEST_FILES_PATH . 'NamespaceCoveredClass.php' => $lines + ); + } + + else if ($test === 'CoverageNoneTest' || $test === 'CoverageNothingTest') { + $expected = array(); + } + + else if ($test === 'CoverageFunctionTest') { + $expected = array( + TEST_FILES_PATH . 'CoveredFunction.php' => $lines + ); + } + + else { + $expected = array(TEST_FILES_PATH . 'CoveredClass.php' => $lines); + } + + $this->assertEquals( + $expected, + $this->getLinesToBeCovered->invoke( + $this->coverage, $test, 'testSomething' + ) + ); + } + + /** + * @covers PHP_CodeCoverage::getLinesToBeCovered + * @covers PHP_CodeCoverage::resolveCoversToReflectionObjects + * @expectedException PHP_CodeCoverage_Exception + */ + public function testGetLinesToBeCovered2() + { + $this->getLinesToBeCovered->invoke( + $this->coverage, 'NotExistingCoveredElementTest', 'testOne' + ); + } + + /** + * @covers PHP_CodeCoverage::getLinesToBeCovered + * @covers PHP_CodeCoverage::resolveCoversToReflectionObjects + * @expectedException PHP_CodeCoverage_Exception + */ + public function testGetLinesToBeCovered3() + { + $this->getLinesToBeCovered->invoke( + $this->coverage, 'NotExistingCoveredElementTest', 'testTwo' + ); + } + + /** + * @covers PHP_CodeCoverage::getLinesToBeCovered + * @covers PHP_CodeCoverage::resolveCoversToReflectionObjects + * @expectedException PHP_CodeCoverage_Exception + */ + public function testGetLinesToBeCovered4() + { + $this->getLinesToBeCovered->invoke( + $this->coverage, 'NotExistingCoveredElementTest', 'testThree' + ); + } + + /** + * @covers PHP_CodeCoverage::getLinesToBeCovered + */ + public function testGetLinesToBeCoveredSkipsNonExistantMethods() + { + $this->assertSame( + array(), + $this->getLinesToBeCovered->invoke( + $this->coverage, + 'NotExistingCoveredElementTest', + 'methodDoesNotExist' + ) + ); + } + + /** + * @covers PHP_CodeCoverage::getLinesToBeCovered + * @expectedException PHP_CodeCoverage_Exception + */ + public function testTwoCoversDefaultClassAnnoationsAreNotAllowed() + { + $this->getLinesToBeCovered->invoke( + $this->coverage, + 'CoverageTwoDefaultClassAnnotations', + 'testSomething' + ); + } + + public function getLinesToBeCoveredProvider() + { + return array( + array( + 'CoverageNoneTest', + array() + ), + array( + 'CoverageClassExtendedTest', + array_merge(range(19, 36), range(2, 17)) + ), + array( + 'CoverageClassTest', + range(19, 36) + ), + array( + 'CoverageMethodTest', + range(31, 35) + ), + array( + 'CoverageMethodOneLineAnnotationTest', + range(31, 35) + ), + array( + 'CoverageNotPrivateTest', + array_merge(range(25, 29), range(31, 35)) + ), + array( + 'CoverageNotProtectedTest', + array_merge(range(21, 23), range(31, 35)) + ), + array( + 'CoverageNotPublicTest', + array_merge(range(21, 23), range(25, 29)) + ), + array( + 'CoveragePrivateTest', + range(21, 23) + ), + array( + 'CoverageProtectedTest', + range(25, 29) + ), + array( + 'CoveragePublicTest', + range(31, 35) + ), + array( + 'CoverageFunctionTest', + range(2, 4) + ), + array( + 'NamespaceCoverageClassExtendedTest', + array_merge(range(21, 38), range(4, 19)) + ), + array( + 'NamespaceCoverageClassTest', + range(21, 38) + ), + array( + 'NamespaceCoverageMethodTest', + range(33, 37) + ), + array( + 'NamespaceCoverageNotPrivateTest', + array_merge(range(27, 31), range(33, 37)) + ), + array( + 'NamespaceCoverageNotProtectedTest', + array_merge(range(23, 25), range(33, 37)) + ), + array( + 'NamespaceCoverageNotPublicTest', + array_merge(range(23, 25), range(27, 31)) + ), + array( + 'NamespaceCoveragePrivateTest', + range(23, 25) + ), + array( + 'NamespaceCoverageProtectedTest', + range(27, 31) + ), + array( + 'NamespaceCoveragePublicTest', + range(33, 37) + ), + array( + 'NamespaceCoverageCoversClassTest', + array_merge(range(23, 25), range(27, 31), range(33, 37), range(6, 8), range(10, 13), range(15, 18)) + ), + array( + 'NamespaceCoverageCoversClassPublicTest', + range(33, 37) + ), + array( + 'CoverageNothingTest', + array() + ) + ); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/TestCase.php b/vendor/phpunit/php-code-coverage/Tests/TestCase.php new file mode 100644 index 0000000..60f1888 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/TestCase.php @@ -0,0 +1,266 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since File available since Release 1.0.0 + */ + +/** + * Abstract base class for test case classes. + * + * @category PHP + * @package CodeCoverage + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-code-coverage + * @since Class available since Release 1.0.0 + */ +abstract class PHP_CodeCoverage_TestCase extends PHPUnit_Framework_TestCase +{ + protected function getXdebugDataForBankAccount() + { + return array( + array( + TEST_FILES_PATH . 'BankAccount.php' => array( + 8 => 1, + 9 => -2, + 13 => -1, + 14 => -1, + 15 => -1, + 16 => -1, + 18 => -1, + 22 => -1, + 24 => -1, + 25 => -2, + 29 => -1, + 31 => -1, + 32 => -2 + ) + ), + array( + TEST_FILES_PATH . 'BankAccount.php' => array( + 8 => 1, + 13 => 1, + 16 => 1, + 29 => 1, + ) + ), + array( + TEST_FILES_PATH . 'BankAccount.php' => array( + 8 => 1, + 13 => 1, + 16 => 1, + 22 => 1, + ) + ), + array( + TEST_FILES_PATH . 'BankAccount.php' => array( + 8 => 1, + 13 => 1, + 14 => 1, + 15 => 1, + 18 => 1, + 22 => 1, + 24 => 1, + 29 => 1, + 31 => 1, + ) + ) + ); + } + + protected function getCoverageForBankAccount() + { + $data = $this->getXdebugDataForBankAccount(); + + $stub = $this->getMock('PHP_CodeCoverage_Driver_Xdebug'); + $stub->expects($this->any()) + ->method('stop') + ->will($this->onConsecutiveCalls( + $data[0], $data[1], $data[2], $data[3] + )); + + $coverage = new PHP_CodeCoverage($stub, new PHP_CodeCoverage_Filter); + + $coverage->start( + new BankAccountTest('testBalanceIsInitiallyZero'), TRUE + ); + $coverage->stop(); + + $coverage->start( + new BankAccountTest('testBalanceCannotBecomeNegative') + ); + $coverage->stop(); + + $coverage->start( + new BankAccountTest('testBalanceCannotBecomeNegative2') + ); + $coverage->stop(); + + $coverage->start( + new BankAccountTest('testDepositWithdrawMoney') + ); + $coverage->stop(); + + return $coverage; + } + + protected function getCoverageForBankAccountForFirstTwoTests() + { + $data = $this->getXdebugDataForBankAccount(); + + $stub = $this->getMock('PHP_CodeCoverage_Driver_Xdebug'); + $stub->expects($this->any()) + ->method('stop') + ->will($this->onConsecutiveCalls( + $data[0], $data[1] + )); + + $coverage = new PHP_CodeCoverage($stub, new PHP_CodeCoverage_Filter); + + $coverage->start( + new BankAccountTest('testBalanceIsInitiallyZero'), TRUE + ); + $coverage->stop(); + + $coverage->start( + new BankAccountTest('testBalanceCannotBecomeNegative') + ); + $coverage->stop(); + + return $coverage; + } + + protected function getCoverageForBankAccountForLastTwoTests() + { + $data = $this->getXdebugDataForBankAccount(); + + $stub = $this->getMock('PHP_CodeCoverage_Driver_Xdebug'); + $stub->expects($this->any()) + ->method('stop') + ->will($this->onConsecutiveCalls( + $data[2], $data[3] + )); + + $coverage = new PHP_CodeCoverage($stub, new PHP_CodeCoverage_Filter); + + $coverage->start( + new BankAccountTest('testBalanceCannotBecomeNegative2'), TRUE + ); + $coverage->stop(); + + $coverage->start( + new BankAccountTest('testDepositWithdrawMoney') + ); + $coverage->stop(); + + return $coverage; + } + + protected function getExpectedDataArrayForBankAccount() + { + return array( + TEST_FILES_PATH . 'BankAccount.php' => array( + 8 => array( + 0 => 'BankAccountTest::testBalanceIsInitiallyZero', + 1 => 'BankAccountTest::testDepositWithdrawMoney' + ), + 9 => NULL, + 13 => array(), + 14 => array(), + 15 => array(), + 16 => array(), + 18 => array(), + 22 => array( + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', + 1 => 'BankAccountTest::testDepositWithdrawMoney' + ), + 24 => array( + 0 => 'BankAccountTest::testDepositWithdrawMoney', + ), + 25 => NULL, + 29 => array( + 0 => 'BankAccountTest::testBalanceCannotBecomeNegative', + 1 => 'BankAccountTest::testDepositWithdrawMoney' + ), + 31 => array( + 0 => 'BankAccountTest::testDepositWithdrawMoney' + ), + 32 => NULL + ) + ); + } + + protected function getCoverageForFileWithIgnoredLines() + { + $coverage = new PHP_CodeCoverage( + $this->setUpXdebugStubForFileWithIgnoredLines(), + new PHP_CodeCoverage_Filter + ); + + $coverage->start('FileWithIgnoredLines', TRUE); + $coverage->stop(); + + return $coverage; + } + + protected function setUpXdebugStubForFileWithIgnoredLines() + { + $stub = $this->getMock('PHP_CodeCoverage_Driver_Xdebug'); + $stub->expects($this->any()) + ->method('stop') + ->will($this->returnValue( + array( + TEST_FILES_PATH . 'source_with_ignore.php' => array( + 2 => 1, + 4 => -1, + 6 => -1, + 7 => 1 + ) + ) + )); + + return $stub; + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/BankAccount-clover.xml b/vendor/phpunit/php-code-coverage/Tests/_files/BankAccount-clover.xml new file mode 100644 index 0000000..578a7cc --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/BankAccount-clover.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/BankAccount.php b/vendor/phpunit/php-code-coverage/Tests/_files/BankAccount.php new file mode 100644 index 0000000..4238c15 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/BankAccount.php @@ -0,0 +1,33 @@ +balance; + } + + protected function setBalance($balance) + { + if ($balance >= 0) { + $this->balance = $balance; + } else { + throw new RuntimeException; + } + } + + public function depositMoney($balance) + { + $this->setBalance($this->getBalance() + $balance); + + return $this->getBalance(); + } + + public function withdrawMoney($balance) + { + $this->setBalance($this->getBalance() - $balance); + + return $this->getBalance(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/BankAccountTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/BankAccountTest.php new file mode 100644 index 0000000..827cd24 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/BankAccountTest.php @@ -0,0 +1,70 @@ +ba = new BankAccount; + } + + /** + * @covers BankAccount::getBalance + */ + public function testBalanceIsInitiallyZero() + { + $this->assertEquals(0, $this->ba->getBalance()); + } + + /** + * @covers BankAccount::withdrawMoney + */ + public function testBalanceCannotBecomeNegative() + { + try { + $this->ba->withdrawMoney(1); + } + + catch (RuntimeException $e) { + $this->assertEquals(0, $this->ba->getBalance()); + + return; + } + + $this->fail(); + } + + /** + * @covers BankAccount::depositMoney + */ + public function testBalanceCannotBecomeNegative2() + { + try { + $this->ba->depositMoney(-1); + } + + catch (RuntimeException $e) { + $this->assertEquals(0, $this->ba->getBalance()); + + return; + } + + $this->fail(); + } + + /** + * @covers BankAccount::getBalance + * @covers BankAccount::depositMoney + * @covers BankAccount::withdrawMoney + */ + public function testDepositWithdrawMoney() + { + $this->assertEquals(0, $this->ba->getBalance()); + $this->ba->depositMoney(1); + $this->assertEquals(1, $this->ba->getBalance()); + $this->ba->withdrawMoney(1); + $this->assertEquals(0, $this->ba->getBalance()); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassExtendedTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassExtendedTest.php new file mode 100644 index 0000000..df12d34 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassExtendedTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassTest.php new file mode 100644 index 0000000..7f569ae --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageClassTest.php @@ -0,0 +1,12 @@ +publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageFunctionTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageFunctionTest.php new file mode 100644 index 0000000..c621fd2 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageFunctionTest.php @@ -0,0 +1,11 @@ +publicMethod(); + } +} + diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageMethodTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageMethodTest.php new file mode 100644 index 0000000..167b3db --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageMethodTest.php @@ -0,0 +1,12 @@ +publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNoneTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNoneTest.php new file mode 100644 index 0000000..0b414c2 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNoneTest.php @@ -0,0 +1,9 @@ +publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPrivateTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPrivateTest.php new file mode 100644 index 0000000..12b56e8 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPrivateTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotProtectedTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotProtectedTest.php new file mode 100644 index 0000000..c69d261 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotProtectedTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPublicTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPublicTest.php new file mode 100644 index 0000000..aebfe4b --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNotPublicTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNothingTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNothingTest.php new file mode 100644 index 0000000..5d5680d --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageNothingTest.php @@ -0,0 +1,13 @@ +publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoveragePrivateTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoveragePrivateTest.php new file mode 100644 index 0000000..f09560d --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoveragePrivateTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageProtectedTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageProtectedTest.php new file mode 100644 index 0000000..9b3acbf --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageProtectedTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoveragePublicTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoveragePublicTest.php new file mode 100644 index 0000000..480a522 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoveragePublicTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoverageTwoDefaultClassAnnotations.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageTwoDefaultClassAnnotations.php new file mode 100644 index 0000000..7c743db --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoverageTwoDefaultClassAnnotations.php @@ -0,0 +1,19 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } + +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoveredClass.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoveredClass.php new file mode 100644 index 0000000..f382ce9 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoveredClass.php @@ -0,0 +1,36 @@ +privateMethod(); + } + + public function publicMethod() + { + $this->protectedMethod(); + } +} + +class CoveredClass extends CoveredParentClass +{ + private function privateMethod() + { + } + + protected function protectedMethod() + { + parent::protectedMethod(); + $this->privateMethod(); + } + + public function publicMethod() + { + parent::publicMethod(); + $this->protectedMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/CoveredFunction.php b/vendor/phpunit/php-code-coverage/Tests/_files/CoveredFunction.php new file mode 100644 index 0000000..9989eb0 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/CoveredFunction.php @@ -0,0 +1,4 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageClassTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageClassTest.php new file mode 100644 index 0000000..63912c0 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageClassTest.php @@ -0,0 +1,12 @@ +publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassPublicTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassPublicTest.php new file mode 100644 index 0000000..d3d48ef --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassPublicTest.php @@ -0,0 +1,16 @@ +publicMethod(); + } +} + diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassTest.php new file mode 100644 index 0000000..79d1010 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageCoversClassTest.php @@ -0,0 +1,21 @@ +publicMethod(); + } +} + diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageMethodTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageMethodTest.php new file mode 100644 index 0000000..35dfb8b --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageMethodTest.php @@ -0,0 +1,12 @@ +publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPrivateTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPrivateTest.php new file mode 100644 index 0000000..552c9ec --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPrivateTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotProtectedTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotProtectedTest.php new file mode 100644 index 0000000..33fc8c7 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotProtectedTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPublicTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPublicTest.php new file mode 100644 index 0000000..ccbc500 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageNotPublicTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePrivateTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePrivateTest.php new file mode 100644 index 0000000..cce7ba9 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePrivateTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageProtectedTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageProtectedTest.php new file mode 100644 index 0000000..dbbcc1c --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoverageProtectedTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePublicTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePublicTest.php new file mode 100644 index 0000000..bf1bff8 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveragePublicTest.php @@ -0,0 +1,12 @@ + + */ + public function testSomething() + { + $o = new Foo\CoveredClass; + $o->publicMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveredClass.php b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveredClass.php new file mode 100644 index 0000000..5bd0ddf --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NamespaceCoveredClass.php @@ -0,0 +1,38 @@ +privateMethod(); + } + + public function publicMethod() + { + $this->protectedMethod(); + } +} + +class CoveredClass extends CoveredParentClass +{ + private function privateMethod() + { + } + + protected function protectedMethod() + { + parent::protectedMethod(); + $this->privateMethod(); + } + + public function publicMethod() + { + parent::publicMethod(); + $this->protectedMethod(); + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/NotExistingCoveredElementTest.php b/vendor/phpunit/php-code-coverage/Tests/_files/NotExistingCoveredElementTest.php new file mode 100644 index 0000000..be07ef4 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/NotExistingCoveredElementTest.php @@ -0,0 +1,24 @@ + + */ + public function testThree() + { + } +} diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/ignored-lines-clover.xml b/vendor/phpunit/php-code-coverage/Tests/_files/ignored-lines-clover.xml new file mode 100644 index 0000000..aeb4172 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/ignored-lines-clover.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-code-coverage/Tests/_files/source_with_ignore.php b/vendor/phpunit/php-code-coverage/Tests/_files/source_with_ignore.php new file mode 100644 index 0000000..51a9268 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/Tests/_files/source_with_ignore.php @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-code-coverage/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php b/vendor/phpunit/php-code-coverage/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php new file mode 100644 index 0000000..bf9d520 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php @@ -0,0 +1,22 @@ +getTokens(); + + if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE || + $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { + + $phpcsFile->addError( + 'Concatenation operator must be surrounded by whitespace', + $stackPtr + ); + } + } +} diff --git a/vendor/phpunit/php-code-coverage/build/PHPCS/ruleset.xml b/vendor/phpunit/php-code-coverage/build/PHPCS/ruleset.xml new file mode 100644 index 0000000..402f214 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/build/PHPCS/ruleset.xml @@ -0,0 +1,35 @@ + + + Sebastian Bergmann's coding standard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-code-coverage/build/phpmd.xml b/vendor/phpunit/php-code-coverage/build/phpmd.xml new file mode 100644 index 0000000..23ecb8b --- /dev/null +++ b/vendor/phpunit/php-code-coverage/build/phpmd.xml @@ -0,0 +1,27 @@ + + + + Sebastian Bergmann's ruleset + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-code-coverage/composer.json b/vendor/phpunit/php-code-coverage/composer.json new file mode 100644 index 0000000..1a4ebe1 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/composer.json @@ -0,0 +1,41 @@ +{ + "name": "phpunit/php-code-coverage", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "type": "library", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-token-stream": ">=1.1.3@stable", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ] +} diff --git a/vendor/phpunit/php-code-coverage/package.xml b/vendor/phpunit/php-code-coverage/package.xml new file mode 100644 index 0000000..cf53a38 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/package.xml @@ -0,0 +1,133 @@ + + + PHP_CodeCoverage + pear.phpunit.de + Library that provides collection, processing, and rendering functionality for PHP code coverage information. + Library that provides collection, processing, and rendering functionality for PHP code coverage information. + + Sebastian Bergmann + sb + sb@sebastian-bergmann.de + yes + + 2012-10-17 + + 1.2.6 + 1.2.0 + + + stable + stable + + The BSD 3-Clause License + http://github.com/sebastianbergmann/php-code-coverage/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.9.4 + + + File_Iterator + pear.phpunit.de + 1.3.0 + + + PHP_TokenStream + pear.phpunit.de + 1.1.3 + + + Text_Template + pear.phpunit.de + 1.1.1 + + + + + dom + + + xdebug + 2.0.5 + + + + + diff --git a/vendor/phpunit/php-code-coverage/phpunit.xml.dist b/vendor/phpunit/php-code-coverage/phpunit.xml.dist new file mode 100644 index 0000000..31c6172 --- /dev/null +++ b/vendor/phpunit/php-code-coverage/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + Tests/PHP + + + + + + + + + + + + PHP + + PHP/CodeCoverage/Autoload.php + + + + diff --git a/vendor/phpunit/php-code-coverage/scripts/auto_append.php b/vendor/phpunit/php-code-coverage/scripts/auto_append.php new file mode 100644 index 0000000..6cd768d --- /dev/null +++ b/vendor/phpunit/php-code-coverage/scripts/auto_append.php @@ -0,0 +1,5 @@ +stop(); + +$writer = new PHP_CodeCoverage_Report_HTML; +$writer->process($coverage, '/tmp/coverage'); diff --git a/vendor/phpunit/php-code-coverage/scripts/auto_prepend.php b/vendor/phpunit/php-code-coverage/scripts/auto_prepend.php new file mode 100644 index 0000000..7a8887a --- /dev/null +++ b/vendor/phpunit/php-code-coverage/scripts/auto_prepend.php @@ -0,0 +1,10 @@ +filter(); + +$filter->addFileToBlacklist(__FILE__); +$filter->addFileToBlacklist(dirname(__FILE__) . '/auto_append.php'); + +$coverage->start($_SERVER['SCRIPT_FILENAME']); diff --git a/vendor/phpunit/php-file-iterator/ChangeLog.markdown b/vendor/phpunit/php-file-iterator/ChangeLog.markdown new file mode 100644 index 0000000..06593b0 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/ChangeLog.markdown @@ -0,0 +1,26 @@ +File_Iterator 1.3 +================= + +This is the list of changes for the File_Iterator 1.3 release series. + +File_Iterator 1.3.3 +------------------- + +* No changes. + +File_Iterator 1.3.2 +------------------- + +* No changes. + +File_Iterator 1.3.1 +------------------- + +* Fixed infinite loop in `File_Iterator_Facade::getCommonPath()` for empty directories. + +File_Iterator 1.3.0 +------------------- + +* Added `File_Iterator_Facade` for the most common use case. +* Moved `File_Iterator_Factory::getFilesAsArray()` to `File_Iterator_Facade::getFilesAsArray()`. +* `File_Iterator_Factory` is no longer static. diff --git a/vendor/phpunit/php-file-iterator/File/Iterator.php b/vendor/phpunit/php-file-iterator/File/Iterator.php new file mode 100644 index 0000000..73f19b9 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/File/Iterator.php @@ -0,0 +1,196 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +/** + * FilterIterator implementation that filters files based on prefix(es) and/or + * suffix(es). Hidden files and files from hidden directories are also filtered. + * + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-file-iterator/tree + * @since Class available since Release 1.0.0 + */ +class File_Iterator extends FilterIterator +{ + const PREFIX = 0; + const SUFFIX = 1; + + /** + * @var array + */ + protected $suffixes = array(); + + /** + * @var array + */ + protected $prefixes = array(); + + /** + * @var array + */ + protected $exclude = array(); + + /** + * @var string + */ + protected $basepath; + + /** + * @param Iterator $iterator + * @param array $suffixes + * @param array $prefixes + * @param array $exclude + * @param string $basepath + */ + public function __construct(Iterator $iterator, array $suffixes = array(), array $prefixes = array(), array $exclude = array(), $basepath = NULL) + { + $exclude = array_filter(array_map('realpath', $exclude)); + + if ($basepath !== NULL) { + $basepath = realpath($basepath); + } + + if ($basepath === FALSE) { + $basepath = NULL; + } else { + foreach ($exclude as &$_exclude) { + $_exclude = str_replace($basepath, '', $_exclude); + } + } + + $this->prefixes = $prefixes; + $this->suffixes = $suffixes; + $this->exclude = $exclude; + $this->basepath = $basepath; + + parent::__construct($iterator); + } + + /** + * @return boolean + */ + public function accept() + { + $current = $this->getInnerIterator()->current(); + $filename = $current->getFilename(); + $realpath = $current->getRealPath(); + + if ($this->basepath !== NULL) { + $realpath = str_replace($this->basepath, '', $realpath); + } + + // Filter files in hidden directories. + if (preg_match('=/\.[^/]*/=', $realpath)) { + return FALSE; + } + + return $this->acceptPath($realpath) && + $this->acceptPrefix($filename) && + $this->acceptSuffix($filename); + } + + /** + * @param string $path + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptPath($path) + { + foreach ($this->exclude as $exclude) { + if (strpos($path, $exclude) === 0) { + return FALSE; + } + } + + return TRUE; + } + + /** + * @param string $filename + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptPrefix($filename) + { + return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); + } + + /** + * @param string $filename + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptSuffix($filename) + { + return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); + } + + /** + * @param string $filename + * @param array $subString + * @param integer $type + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptSubString($filename, array $subStrings, $type) + { + if (empty($subStrings)) { + return TRUE; + } + + $matched = FALSE; + + foreach ($subStrings as $string) { + if (($type == self::PREFIX && strpos($filename, $string) === 0) || + ($type == self::SUFFIX && + substr($filename, -1 * strlen($string)) == $string)) { + $matched = TRUE; + break; + } + } + + return $matched; + } +} diff --git a/vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php b/vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php new file mode 100644 index 0000000..fcb2105 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.3.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + 'file_iterator' => '/Iterator.php', + 'file_iterator_facade' => '/Iterator/Facade.php', + 'file_iterator_factory' => '/Iterator/Factory.php' + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php.in b/vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php.in new file mode 100644 index 0000000..20d58b9 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/File/Iterator/Autoload.php.in @@ -0,0 +1,64 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.3.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-file-iterator/File/Iterator/Facade.php b/vendor/phpunit/php-file-iterator/File/Iterator/Facade.php new file mode 100644 index 0000000..d30283e --- /dev/null +++ b/vendor/phpunit/php-file-iterator/File/Iterator/Facade.php @@ -0,0 +1,161 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.3.0 + */ + +/** + * Façade implementation that uses File_Iterator_Factory to create a + * File_Iterator that operates on an AppendIterator that contains an + * RecursiveDirectoryIterator for each given path. The list of unique + * files is returned as an array. + * + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-file-iterator/tree + * @since Class available since Release 1.3.0 + */ +class File_Iterator_Facade +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @param boolean $commonPath + * @return array + */ + public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = array(), $commonPath = FALSE) + { + if (is_string($paths)) { + $paths = array($paths); + } + + $factory = new File_Iterator_Factory; + $iterator = $factory->getFileIterator( + $paths, $suffixes, $prefixes, $exclude + ); + + $files = array(); + + foreach ($iterator as $file) { + $file = $file->getRealPath(); + + if ($file) { + $files[] = $file; + } + } + + foreach ($paths as $path) { + if (is_file($path)) { + $files[] = realpath($path); + } + } + + $files = array_unique($files); + sort($files); + + if ($commonPath) { + return array( + 'commonPath' => $this->getCommonPath($files), + 'files' => $files + ); + } else { + return $files; + } + } + + /** + * Returns the common path of a set of files. + * + * @param array $files + * @return string + */ + protected function getCommonPath(array $files) + { + $count = count($files); + + if ($count == 0) { + return ''; + } + + if ($count == 1) { + return dirname($files[0]) . DIRECTORY_SEPARATOR; + } + + $_files = array(); + + foreach ($files as $file) { + $_files[] = $_fileParts = explode(DIRECTORY_SEPARATOR, $file); + + if (empty($_fileParts[0])) { + $_fileParts[0] = DIRECTORY_SEPARATOR; + } + } + + $common = ''; + $done = FALSE; + $j = 0; + $count--; + + while (!$done) { + for ($i = 0; $i < $count; $i++) { + if ($_files[$i][$j] != $_files[$i+1][$j]) { + $done = TRUE; + break; + } + } + + if (!$done) { + $common .= $_files[0][$j]; + + if ($j > 0) { + $common .= DIRECTORY_SEPARATOR; + } + } + + $j++; + } + + return DIRECTORY_SEPARATOR . $common; + } +} diff --git a/vendor/phpunit/php-file-iterator/File/Iterator/Factory.php b/vendor/phpunit/php-file-iterator/File/Iterator/Factory.php new file mode 100644 index 0000000..47a50ae --- /dev/null +++ b/vendor/phpunit/php-file-iterator/File/Iterator/Factory.php @@ -0,0 +1,120 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.1.0 + */ + +/** + * Factory Method implementation that creates a File_Iterator that operates on + * an AppendIterator that contains an RecursiveDirectoryIterator for each given + * path. + * + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-file-iterator/tree + * @since Class available since Release 1.1.0 + */ +class File_Iterator_Factory +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @return AppendIterator + */ + public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array()) + { + if (is_string($paths)) { + $paths = array($paths); + } + + $_paths = array(); + + foreach ($paths as $path) { + if ($locals = glob($path, GLOB_ONLYDIR)) { + $_paths = array_merge($_paths, $locals); + } else { + $_paths[] = $path; + } + } + + $paths = $_paths; + unset($_paths); + + if (is_string($prefixes)) { + if ($prefixes != '') { + $prefixes = array($prefixes); + } else { + $prefixes = array(); + } + } + + if (is_string($suffixes)) { + if ($suffixes != '') { + $suffixes = array($suffixes); + } else { + $suffixes = array(); + } + } + + $iterator = new AppendIterator; + + foreach ($paths as $path) { + if (is_dir($path)) { + $iterator->append( + new File_Iterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path) + ), + $suffixes, + $prefixes, + $exclude, + $path + ) + ); + } + } + + return $iterator; + } +} diff --git a/vendor/phpunit/php-file-iterator/LICENSE b/vendor/phpunit/php-file-iterator/LICENSE new file mode 100644 index 0000000..58c5743 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/LICENSE @@ -0,0 +1,33 @@ +File_Iterator + +Copyright (c) 2009-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/php-file-iterator/README.markdown b/vendor/phpunit/php-file-iterator/README.markdown new file mode 100644 index 0000000..c8971c2 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/README.markdown @@ -0,0 +1,23 @@ +File_Iterator +============= + +Installation +------------ + +File_Iterator should be installed using the [PEAR Installer](http://pear.php.net/). This installer is the backbone of PEAR, which provides a distribution system for PHP packages, and is shipped with every release of PHP since version 4.3.0. + +The PEAR channel (`pear.phpunit.de`) that is used to distribute File_Iterator needs to be registered with the local PEAR environment: + + sb@ubuntu ~ % pear channel-discover pear.phpunit.de + Adding Channel "pear.phpunit.de" succeeded + Discovery of channel "pear.phpunit.de" succeeded + +This has to be done only once. Now the PEAR Installer can be used to install packages from the PHPUnit channel: + + sb@vmware ~ % pear install phpunit/File_Iterator + downloading File_Iterator-1.1.1.tgz ... + Starting to download File_Iterator-1.1.1.tgz (3,173 bytes) + ....done: 3,173 bytes + install ok: channel://pear.phpunit.de/File_Iterator-1.1.1 + +After the installation you can find the File_Iterator source files inside your local PEAR directory; the path is usually `/usr/lib/php/File`. diff --git a/vendor/phpunit/php-file-iterator/build.xml b/vendor/phpunit/php-file-iterator/build.xml new file mode 100644 index 0000000..29d362f --- /dev/null +++ b/vendor/phpunit/php-file-iterator/build.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-file-iterator/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php b/vendor/phpunit/php-file-iterator/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php new file mode 100644 index 0000000..bf9d520 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php @@ -0,0 +1,22 @@ +getTokens(); + + if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE || + $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { + + $phpcsFile->addError( + 'Concatenation operator must be surrounded by whitespace', + $stackPtr + ); + } + } +} diff --git a/vendor/phpunit/php-file-iterator/build/PHPCS/ruleset.xml b/vendor/phpunit/php-file-iterator/build/PHPCS/ruleset.xml new file mode 100644 index 0000000..402f214 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/build/PHPCS/ruleset.xml @@ -0,0 +1,35 @@ + + + Sebastian Bergmann's coding standard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-file-iterator/build/phpmd.xml b/vendor/phpunit/php-file-iterator/build/phpmd.xml new file mode 100644 index 0000000..23ecb8b --- /dev/null +++ b/vendor/phpunit/php-file-iterator/build/phpmd.xml @@ -0,0 +1,27 @@ + + + + Sebastian Bergmann's ruleset + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-file-iterator/composer.json b/vendor/phpunit/php-file-iterator/composer.json new file mode 100644 index 0000000..aab32bc --- /dev/null +++ b/vendor/phpunit/php-file-iterator/composer.json @@ -0,0 +1,33 @@ +{ + "name": "phpunit/php-file-iterator", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "type": "library", + "keywords": [ + "iterator", + "filesystem" + ], + "homepage": "http://www.phpunit.de/", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "classmap": [ + "File/" + ] + }, + "include-path": [ + "" + ] +} diff --git a/vendor/phpunit/php-file-iterator/package-composer.json b/vendor/phpunit/php-file-iterator/package-composer.json new file mode 100644 index 0000000..c71bad7 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/package-composer.json @@ -0,0 +1,19 @@ +{ + "name": "phpunit/php-file-iterator", + "keywords": [ "iterator", "filesystem" ], + "license": "BSD-3-Clause", + "homepage": "http://www.phpunit.de/", + "dependency_map": {}, + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "autoload": { + "classmap": [ "File/" ] + }, + "include_path": [ + "" + ], + "version": false, + "time": false +} \ No newline at end of file diff --git a/vendor/phpunit/php-file-iterator/package.xml b/vendor/phpunit/php-file-iterator/package.xml new file mode 100644 index 0000000..e08bfd5 --- /dev/null +++ b/vendor/phpunit/php-file-iterator/package.xml @@ -0,0 +1,65 @@ + + + File_Iterator + pear.phpunit.de + FilterIterator implementation that filters files based on a list of suffixes. + FilterIterator implementation that filters files based on a list of suffixes. + + Sebastian Bergmann + sb + sb@sebastian-bergmann.de + yes + + 2012-10-05 + + 1.3.3 + 1.3.0 + + + stable + stable + + The BSD 3-Clause License + http://github.com/sebastianbergmann/php-file-iterator/tree + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.9.4 + + + + + diff --git a/vendor/phpunit/php-text-template/ChangeLog.markdown b/vendor/phpunit/php-text-template/ChangeLog.markdown new file mode 100644 index 0000000..af2423b --- /dev/null +++ b/vendor/phpunit/php-text-template/ChangeLog.markdown @@ -0,0 +1,19 @@ +Text_Template 1.1 +================= + +This is the list of changes for the Text_Template 1.1 release series. + +Text_Template 1.1.3 +------------------- + +* No changes. + +Text_Template 1.1.2 +------------------- + +* No changes. + +Text_Template 1.1.1 +------------------- + +* No changes. diff --git a/vendor/phpunit/php-text-template/LICENSE b/vendor/phpunit/php-text-template/LICENSE new file mode 100644 index 0000000..af2a713 --- /dev/null +++ b/vendor/phpunit/php-text-template/LICENSE @@ -0,0 +1,33 @@ +Text_Template + +Copyright (c) 2009-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/php-text-template/README.markdown b/vendor/phpunit/php-text-template/README.markdown new file mode 100644 index 0000000..0d60da9 --- /dev/null +++ b/vendor/phpunit/php-text-template/README.markdown @@ -0,0 +1,23 @@ +Text_Template +============= + +Installation +------------ + +Text_Template should be installed using the [PEAR Installer](http://pear.php.net/). This installer is the backbone of PEAR, which provides a distribution system for PHP packages, and is shipped with every release of PHP since version 4.3.0. + +The PEAR channel (`pear.phpunit.de`) that is used to distribute Text_Template needs to be registered with the local PEAR environment: + + sb@ubuntu ~ % pear channel-discover pear.phpunit.de + Adding Channel "pear.phpunit.de" succeeded + Discovery of channel "pear.phpunit.de" succeeded + +This has to be done only once. Now the PEAR Installer can be used to install packages from the PHPUnit channel: + + sb@vmware ~ % pear install phpunit/Text_Template + downloading Text_Template-1.0.0.tgz ... + Starting to download Text_Template-1.0.0.tgz (2,493 bytes) + ....done: 2,493 bytes + install ok: channel://pear.phpunit.de/Text_Template-1.0.0 + +After the installation you can find the Text_Template source files inside your local PEAR directory; the path is usually `/usr/lib/php/Text`. diff --git a/vendor/phpunit/php-text-template/Text/Template.php b/vendor/phpunit/php-text-template/Text/Template.php new file mode 100644 index 0000000..3a6ab84 --- /dev/null +++ b/vendor/phpunit/php-text-template/Text/Template.php @@ -0,0 +1,153 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Text + * @package Template + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-text-template + * @since File available since Release 1.0.0 + */ + +/** + * A simple template engine. + * + * @category Text + * @package Template + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-text-template + * @since Class available since Release 1.0.0 + */ +class Text_Template +{ + /** + * @var string + */ + protected $template = ''; + + /** + * @var array + */ + protected $values = array(); + + /** + * Constructor. + * + * @param string $file + * @throws InvalidArgumentException + */ + public function __construct($file = '') + { + $this->setFile($file); + } + + /** + * Sets the template file. + * + * @param string $file + * @throws InvalidArgumentException + */ + public function setFile($file) + { + $distFile = $file . '.dist'; + + if (file_exists($file)) { + $this->template = file_get_contents($file); + } + + else if (file_exists($distFile)) { + $this->template = file_get_contents($distFile); + } + + else { + throw new InvalidArgumentException( + 'Template file could not be loaded.' + ); + } + } + + /** + * Sets one or more template variables. + * + * @param array $values + * @param boolean $merge + */ + public function setVar(array $values, $merge = TRUE) + { + if (!$merge || empty($this->values)) { + $this->values = $values; + } else { + $this->values = array_merge($this->values, $values); + } + } + + /** + * Renders the template and returns the result. + * + * @return string + */ + public function render() + { + $keys = array(); + + foreach ($this->values as $key => $value) { + $keys[] = '{' . $key . '}'; + } + + return str_replace($keys, $this->values, $this->template); + } + + /** + * Renders the template and writes the result to a file. + * + * @param string $target + */ + public function renderTo($target) + { + $fp = @fopen($target, 'wt'); + + if ($fp) { + fwrite($fp, $this->render()); + fclose($fp); + } else { + throw new RuntimeException('Could not write to ' . $target . '.'); + } + } +} diff --git a/vendor/phpunit/php-text-template/Text/Template/Autoload.php b/vendor/phpunit/php-text-template/Text/Template/Autoload.php new file mode 100644 index 0000000..90ee111 --- /dev/null +++ b/vendor/phpunit/php-text-template/Text/Template/Autoload.php @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Text + * @package Template + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-text-template + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + 'text_template' => '/Template.php' + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-text-template/Text/Template/Autoload.php.in b/vendor/phpunit/php-text-template/Text/Template/Autoload.php.in new file mode 100644 index 0000000..1837403 --- /dev/null +++ b/vendor/phpunit/php-text-template/Text/Template/Autoload.php.in @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Text + * @package Template + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-text-template + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-text-template/build.xml b/vendor/phpunit/php-text-template/build.xml new file mode 100644 index 0000000..3be9137 --- /dev/null +++ b/vendor/phpunit/php-text-template/build.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-text-template/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php b/vendor/phpunit/php-text-template/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php new file mode 100644 index 0000000..bf9d520 --- /dev/null +++ b/vendor/phpunit/php-text-template/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php @@ -0,0 +1,22 @@ +getTokens(); + + if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE || + $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { + + $phpcsFile->addError( + 'Concatenation operator must be surrounded by whitespace', + $stackPtr + ); + } + } +} diff --git a/vendor/phpunit/php-text-template/build/PHPCS/ruleset.xml b/vendor/phpunit/php-text-template/build/PHPCS/ruleset.xml new file mode 100644 index 0000000..402f214 --- /dev/null +++ b/vendor/phpunit/php-text-template/build/PHPCS/ruleset.xml @@ -0,0 +1,35 @@ + + + Sebastian Bergmann's coding standard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-text-template/build/phpmd.xml b/vendor/phpunit/php-text-template/build/phpmd.xml new file mode 100644 index 0000000..23ecb8b --- /dev/null +++ b/vendor/phpunit/php-text-template/build/phpmd.xml @@ -0,0 +1,27 @@ + + + + Sebastian Bergmann's ruleset + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-text-template/composer.json b/vendor/phpunit/php-text-template/composer.json new file mode 100644 index 0000000..02c58e4 --- /dev/null +++ b/vendor/phpunit/php-text-template/composer.json @@ -0,0 +1,32 @@ +{ + "name": "phpunit/php-text-template", + "description": "Simple template engine.", + "type": "library", + "keywords": [ + "template" + ], + "homepage": "http://www.phpunit.de/", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "classmap": [ + "Text/" + ] + }, + "include-path": [ + "" + ] +} diff --git a/vendor/phpunit/php-text-template/package-composer.json b/vendor/phpunit/php-text-template/package-composer.json new file mode 100644 index 0000000..ab2c1a8 --- /dev/null +++ b/vendor/phpunit/php-text-template/package-composer.json @@ -0,0 +1,19 @@ +{ + "name": "phpunit/php-text-template", + "keywords": [ "template" ], + "license": "BSD-3-Clause", + "homepage": "http://www.phpunit.de/", + "dependency_map": { }, + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "autoload": { + "classmap": [ "Text/" ] + }, + "include_path": [ + "" + ], + "version": false, + "time": false +} \ No newline at end of file diff --git a/vendor/phpunit/php-text-template/package.xml b/vendor/phpunit/php-text-template/package.xml new file mode 100644 index 0000000..e758cdf --- /dev/null +++ b/vendor/phpunit/php-text-template/package.xml @@ -0,0 +1,59 @@ + + + Text_Template + pear.phpunit.de + Simple template engine. + Simple template engine. + + Sebastian Bergmann + sb + sb@sebastian-bergmann.de + yes + + 2012-10-05 + + 1.1.3 + 1.1.0 + + + stable + stable + + The BSD 3-Clause License + http://github.com/sebastianbergmann/php-text-template + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.9.4 + + + + + diff --git a/vendor/phpunit/php-timer/ChangeLog.markdown b/vendor/phpunit/php-timer/ChangeLog.markdown new file mode 100644 index 0000000..8d6a052 --- /dev/null +++ b/vendor/phpunit/php-timer/ChangeLog.markdown @@ -0,0 +1,29 @@ +PHP_Timer 1.0 +============= + +This is the list of changes for the PHP_Timer 1.0 release series. + +PHP_Timer 1.0.4 +--------------- + +* No changes. + +PHP_Timer 1.0.3 +--------------- + +* No changes. + +PHP_Timer 1.0.2 +--------------- + +* `$_SERVER['REQUEST_TIME_FLOAT']` is used when available. + +PHP_Timer 1.0.1 +--------------- + +* No changes. + +PHP_Timer 1.0.0 +--------------- + +* Initial release. diff --git a/vendor/phpunit/php-timer/LICENSE b/vendor/phpunit/php-timer/LICENSE new file mode 100644 index 0000000..b51fc64 --- /dev/null +++ b/vendor/phpunit/php-timer/LICENSE @@ -0,0 +1,33 @@ +PHP_Timer + +Copyright (c) 2010-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/php-timer/PHP/Timer.php b/vendor/phpunit/php-timer/PHP/Timer.php new file mode 100644 index 0000000..0f2cc2b --- /dev/null +++ b/vendor/phpunit/php-timer/PHP/Timer.php @@ -0,0 +1,159 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP + * @subpackage Timer + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-timer + * @since File available since Release 1.0.0 + */ + +/** + * Utility class for timing. + * + * @package PHP + * @subpackage Timer + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-timer + * @since Class available since Release 1.0.0 + */ +class PHP_Timer +{ + /** + * @var array + */ + protected static $startTimes = array(); + + /** + * @var float + */ + public static $requestTime; + + /** + * Starts the timer. + */ + public static function start() + { + array_push(self::$startTimes, microtime(TRUE)); + } + + /** + * Stops the timer and returns the elapsed time. + * + * @return float + */ + public static function stop() + { + return microtime(TRUE) - array_pop(self::$startTimes); + } + + /** + * Formats the elapsed time as a string. + * + * @param float $time + * @return string + */ + public static function secondsToTimeString($time) + { + $buffer = ''; + + $hours = sprintf('%02d', ($time >= 3600) ? floor($time / 3600) : 0); + $minutes = sprintf( + '%02d', + ($time >= 60) ? floor($time / 60) - 60 * $hours : 0 + ); + $seconds = sprintf('%02d', $time - 60 * 60 * $hours - 60 * $minutes); + + if ($hours == 0 && $minutes == 0) { + $seconds = sprintf('%1d', $seconds); + + $buffer .= $seconds . ' second'; + + if ($seconds != '1') { + $buffer .= 's'; + } + } else { + if ($hours > 0) { + $buffer = $hours . ':'; + } + + $buffer .= $minutes . ':' . $seconds; + } + + return $buffer; + } + + /** + * Formats the elapsed time since the start of the request as a string. + * + * @return string + */ + public static function timeSinceStartOfRequest() + { + return self::secondsToTimeString(time() - self::$requestTime); + } + + /** + * Returns the resources (time, memory) of the request as a string. + * + * @return string + */ + public static function resourceUsage() + { + return sprintf( + 'Time: %s, Memory: %4.2fMb', + self::timeSinceStartOfRequest(), + memory_get_peak_usage(TRUE) / 1048576 + ); + } +} + +if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { + PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME_FLOAT']; +} + +else if (isset($_SERVER['REQUEST_TIME'])) { + PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME']; +} + +else { + PHP_Timer::$requestTime = time(); +} \ No newline at end of file diff --git a/vendor/phpunit/php-timer/PHP/Timer/Autoload.php b/vendor/phpunit/php-timer/PHP/Timer/Autoload.php new file mode 100644 index 0000000..0184b9d --- /dev/null +++ b/vendor/phpunit/php-timer/PHP/Timer/Autoload.php @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP + * @subpackage Timer + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-timer + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + 'php_timer' => '/Timer.php' + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-timer/PHP/Timer/Autoload.php.in b/vendor/phpunit/php-timer/PHP/Timer/Autoload.php.in new file mode 100644 index 0000000..77e7b03 --- /dev/null +++ b/vendor/phpunit/php-timer/PHP/Timer/Autoload.php.in @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP + * @subpackage Timer + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-timer + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-timer/README.markdown b/vendor/phpunit/php-timer/README.markdown new file mode 100644 index 0000000..9ff8f78 --- /dev/null +++ b/vendor/phpunit/php-timer/README.markdown @@ -0,0 +1,23 @@ +PHP_Timer +========= + +Installation +------------ + +PHP_Timer should be installed using the [PEAR Installer](http://pear.php.net/). This installer is the backbone of PEAR, which provides a distribution system for PHP packages, and is shipped with every release of PHP since version 4.3.0. + +The PEAR channel (`pear.phpunit.de`) that is used to distribute PHP_Timer needs to be registered with the local PEAR environment: + + sb@ubuntu ~ % pear channel-discover pear.phpunit.de + Adding Channel "pear.phpunit.de" succeeded + Discovery of channel "pear.phpunit.de" succeeded + +This has to be done only once. Now the PEAR Installer can be used to install packages from the PHPUnit channel: + + sb@vmware ~ % pear install phpunit/PHP_Timer + downloading PHP_Timer-1.0.0.tgz ... + Starting to download PHP_Timer-1.0.0.tgz (2,536 bytes) + ....done: 2,536 bytes + install ok: channel://pear.phpunit.de/PHP_Timer-1.0.0 + +After the installation you can find the PHP_Timer source files inside your local PEAR directory; the path is usually `/usr/lib/php/PHP`. diff --git a/vendor/phpunit/php-timer/Tests/TimerTest.php b/vendor/phpunit/php-timer/Tests/TimerTest.php new file mode 100644 index 0000000..1582d3b --- /dev/null +++ b/vendor/phpunit/php-timer/Tests/TimerTest.php @@ -0,0 +1,108 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP + * @subpackage Timer + * @author Sebastian Bergmann + * @copyright 2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-timer + * @since File available since Release 1.0.0 + */ + +require_once dirname(dirname(__FILE__)) . '/PHP/Timer.php'; + +/** + * Tests for PHP_Timer. + * + * @package PHP + * @subpackage Timer + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-timer + * @since Class available since Release 1.0.0 + */ +class PHP_TimerTest extends PHPUnit_Framework_TestCase +{ + /** + * @covers PHP_Timer::start + * @covers PHP_Timer::stop + */ + public function testStartStop() + { + PHP_Timer::start(); + $this->assertInternalType('float', PHP_Timer::stop()); + } + + /** + * @covers PHP_Timer::secondsToTimeString + */ + public function testSecondsToTimeString() + { + $this->assertEquals('0 seconds', PHP_Timer::secondsToTimeString(0)); + $this->assertEquals('1 second', PHP_Timer::secondsToTimeString(1)); + $this->assertEquals('2 seconds', PHP_Timer::secondsToTimeString(2)); + $this->assertEquals('01:00', PHP_Timer::secondsToTimeString(60)); + $this->assertEquals('01:01', PHP_Timer::secondsToTimeString(61)); + $this->assertEquals('02:00', PHP_Timer::secondsToTimeString(120)); + $this->assertEquals('02:01', PHP_Timer::secondsToTimeString(121)); + $this->assertEquals('01:00:00', PHP_Timer::secondsToTimeString(3600)); + $this->assertEquals('01:00:01', PHP_Timer::secondsToTimeString(3601)); + } + + /** + * @covers PHP_Timer::timeSinceStartOfRequest + */ + public function testTimeSinceStartOfRequest() + { + $this->assertStringMatchesFormat( + '%i %s', PHP_Timer::timeSinceStartOfRequest() + ); + } + + + /** + * @covers PHP_Timer::resourceUsage + */ + public function testResourceUsage() + { + $this->assertStringMatchesFormat( + 'Time: %s, Memory: %s', PHP_Timer::resourceUsage() + ); + } +} diff --git a/vendor/phpunit/php-timer/build.xml b/vendor/phpunit/php-timer/build.xml new file mode 100644 index 0000000..32469d0 --- /dev/null +++ b/vendor/phpunit/php-timer/build.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-timer/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php b/vendor/phpunit/php-timer/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php new file mode 100644 index 0000000..bf9d520 --- /dev/null +++ b/vendor/phpunit/php-timer/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php @@ -0,0 +1,22 @@ +getTokens(); + + if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE || + $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { + + $phpcsFile->addError( + 'Concatenation operator must be surrounded by whitespace', + $stackPtr + ); + } + } +} diff --git a/vendor/phpunit/php-timer/build/PHPCS/ruleset.xml b/vendor/phpunit/php-timer/build/PHPCS/ruleset.xml new file mode 100644 index 0000000..402f214 --- /dev/null +++ b/vendor/phpunit/php-timer/build/PHPCS/ruleset.xml @@ -0,0 +1,35 @@ + + + Sebastian Bergmann's coding standard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-timer/build/phpmd.xml b/vendor/phpunit/php-timer/build/phpmd.xml new file mode 100644 index 0000000..23ecb8b --- /dev/null +++ b/vendor/phpunit/php-timer/build/phpmd.xml @@ -0,0 +1,27 @@ + + + + Sebastian Bergmann's ruleset + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-timer/composer.json b/vendor/phpunit/php-timer/composer.json new file mode 100644 index 0000000..b10a398 --- /dev/null +++ b/vendor/phpunit/php-timer/composer.json @@ -0,0 +1,32 @@ +{ + "name": "phpunit/php-timer", + "description": "Utility class for timing", + "type": "library", + "keywords": [ + "timer" + ], + "homepage": "http://www.phpunit.de/", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ] +} diff --git a/vendor/phpunit/php-timer/package-composer.json b/vendor/phpunit/php-timer/package-composer.json new file mode 100644 index 0000000..7bdde76 --- /dev/null +++ b/vendor/phpunit/php-timer/package-composer.json @@ -0,0 +1,19 @@ +{ + "name": "phpunit/php-timer", + "keywords": [ "timer" ], + "license": "BSD-3-Clause", + "homepage": "http://www.phpunit.de/", + "dependency_map": { }, + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "autoload": { + "classmap": [ "PHP/" ] + }, + "include_path": [ + "" + ], + "version": false, + "time": false +} \ No newline at end of file diff --git a/vendor/phpunit/php-timer/package.xml b/vendor/phpunit/php-timer/package.xml new file mode 100644 index 0000000..c90fbf2 --- /dev/null +++ b/vendor/phpunit/php-timer/package.xml @@ -0,0 +1,59 @@ + + + PHP_Timer + pear.phpunit.de + Utility class for timing + Utility class for timing + + Sebastian Bergmann + sb + sb@sebastian-bergmann.de + yes + + 2012-10-05 + + 1.0.4 + 1.0.0 + + + stable + stable + + The BSD 3-Clause License + http://github.com/sebastianbergmann/php-timer/blob/master/README.markdown + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.9.2 + + + + + diff --git a/vendor/phpunit/php-timer/phpunit.xml.dist b/vendor/phpunit/php-timer/phpunit.xml.dist new file mode 100644 index 0000000..ee82ac8 --- /dev/null +++ b/vendor/phpunit/php-timer/phpunit.xml.dist @@ -0,0 +1,26 @@ + + + + + + Tests + + + + + + + + + + + + PHP + + PHP/Timer/Autoload.php + + + + diff --git a/vendor/phpunit/php-token-stream/ChangeLog.markdown b/vendor/phpunit/php-token-stream/ChangeLog.markdown new file mode 100644 index 0000000..0fb8fff --- /dev/null +++ b/vendor/phpunit/php-token-stream/ChangeLog.markdown @@ -0,0 +1,35 @@ +PHP_TokenStream 1.1 +=================== + +This is the list of changes for the PHP_TokenStream 1.1 release series. + +PHP_TokenStream 1.1.5 +--------------------- + +* No changes. + +PHP_TokenStream 1.1.4 +--------------------- + +* No changes. + +PHP_TokenStream 1.1.3 +--------------------- + +* Added class for the `T_TRAIT_C` token that was added in PHP 5.4. + +PHP_TokenStream 1.1.2 +--------------------- + +* Added classes for the `T_CALLABLE` and `T_INSTEADOF` tokens that were added in PHP 5.4. +* Added support for namespaced functions. + +PHP_TokenStream 1.1.1 +--------------------- + +* Fixed issue #19: Notice in `PHP_Token_INTERFACE::hasInterfaces()`. + +PHP_TokenStream 1.1.0 +--------------------- + +* Moved `phptok` tool to separate package. diff --git a/vendor/phpunit/php-token-stream/LICENSE b/vendor/phpunit/php-token-stream/LICENSE new file mode 100644 index 0000000..471af20 --- /dev/null +++ b/vendor/phpunit/php-token-stream/LICENSE @@ -0,0 +1,33 @@ +PHP_TokenStream + +Copyright (c) 2009-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/php-token-stream/PHP/Token.php b/vendor/phpunit/php-token-stream/PHP/Token.php new file mode 100644 index 0000000..ebd4ce7 --- /dev/null +++ b/vendor/phpunit/php-token-stream/PHP/Token.php @@ -0,0 +1,717 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +/** + * A PHP token. + * + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/tree + * @since Class available since Release 1.0.0 + */ +abstract class PHP_Token +{ + /** + * @var string + */ + protected $text; + + /** + * @var integer + */ + protected $line; + + /** + * @var PHP_Token_Stream + */ + protected $tokenStream; + + /** + * @var integer + */ + protected $id; + + /** + * Constructor. + * + * @param string $text + * @param integer $line + * @param PHP_Token_Stream $tokenStream + * @param integer $id + */ + public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id) + { + $this->text = $text; + $this->line = $line; + $this->tokenStream = $tokenStream; + $this->id = $id; + } + + /** + * @return string + */ + public function __toString() + { + return $this->text; + } + + /** + * @return integer + */ + public function getLine() + { + return $this->line; + } +} + +abstract class PHP_TokenWithScope extends PHP_Token +{ + protected $endTokenId; + + /** + * Get the docblock for this token + * + * This method will fetch the docblock belonging to the current token. The + * docblock must be placed on the line directly above the token to be + * recognized. + * + * @return string|null Returns the docblock as a string if found + */ + public function getDocblock() + { + $tokens = $this->tokenStream->tokens(); + $currentLineNumber = $tokens[$this->id]->getLine(); + $prevLineNumber = $currentLineNumber - 1; + + for ($i = $this->id - 1; $i; $i--) { + if (!isset($tokens[$i])) { + return; + } + + if ($tokens[$i] instanceof PHP_Token_FUNCTION || + $tokens[$i] instanceof PHP_Token_CLASS || + $tokens[$i] instanceof PHP_Token_TRAIT) { + // Some other trait, class or function, no docblock can be + // used for the current token + break; + } + + $line = $tokens[$i]->getLine(); + + if ($line == $currentLineNumber || + ($line == $prevLineNumber && + $tokens[$i] instanceof PHP_Token_WHITESPACE)) { + continue; + } + + if ($line < $currentLineNumber && + !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) { + break; + } + + return (string)$tokens[$i]; + } + } + + public function getEndTokenId() + { + $block = 0; + $i = $this->id; + $tokens = $this->tokenStream->tokens(); + + while ($this->endTokenId === NULL && isset($tokens[$i])) { + if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || + $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { + $block++; + } + + else if ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) { + $block--; + + if ($block === 0) { + $this->endTokenId = $i; + } + } + + else if (($this instanceof PHP_Token_FUNCTION || + $this instanceof PHP_Token_NAMESPACE) && + $tokens[$i] instanceof PHP_Token_SEMICOLON) { + if ($block === 0) { + $this->endTokenId = $i; + } + } + + $i++; + } + + if ($this->endTokenId === NULL) { + $this->endTokenId = $this->id; + } + + return $this->endTokenId; + } + + public function getEndLine() + { + return $this->tokenStream[$this->getEndTokenId()]->getLine(); + } + +} + +abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope { + + public function getVisibility() + { + $tokens = $this->tokenStream->tokens(); + + for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { + if (isset($tokens[$i]) && + ($tokens[$i] instanceof PHP_Token_PRIVATE || + $tokens[$i] instanceof PHP_Token_PROTECTED || + $tokens[$i] instanceof PHP_Token_PUBLIC)) { + return strtolower( + str_replace('PHP_Token_', '', get_class($tokens[$i])) + ); + } + if (isset($tokens[$i]) && + !($tokens[$i] instanceof PHP_Token_STATIC || + $tokens[$i] instanceof PHP_Token_FINAL || + $tokens[$i] instanceof PHP_Token_ABSTRACT)) { + // no keywords; stop visibility search + break; + } + } + } + + public function getKeywords() + { + $keywords = array(); + $tokens = $this->tokenStream->tokens(); + + for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { + if (isset($tokens[$i]) && + ($tokens[$i] instanceof PHP_Token_PRIVATE || + $tokens[$i] instanceof PHP_Token_PROTECTED || + $tokens[$i] instanceof PHP_Token_PUBLIC)) { + continue; + } + + if (isset($tokens[$i]) && + ($tokens[$i] instanceof PHP_Token_STATIC || + $tokens[$i] instanceof PHP_Token_FINAL || + $tokens[$i] instanceof PHP_Token_ABSTRACT)) { + $keywords[] = strtolower( + str_replace('PHP_Token_', '', get_class($tokens[$i])) + ); + } + } + + return implode(',', $keywords); + } + +} + +abstract class PHP_Token_Includes extends PHP_Token +{ + protected $name; + protected $type; + + public function getName() + { + if ($this->name !== NULL) { + return $this->name; + } + + $tokens = $this->tokenStream->tokens(); + + if ($tokens[$this->id+2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) { + $this->name = trim($tokens[$this->id+2], "'\""); + $this->type = strtolower( + str_replace('PHP_Token_', '', get_class($tokens[$this->id])) + ); + } + + return $this->name; + } + + public function getType() + { + $this->getName(); + return $this->type; + } +} + +class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes {} +class PHP_Token_REQUIRE extends PHP_Token_Includes {} +class PHP_Token_EVAL extends PHP_Token {} +class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes {} +class PHP_Token_INCLUDE extends PHP_Token_Includes {} +class PHP_Token_LOGICAL_OR extends PHP_Token {} +class PHP_Token_LOGICAL_XOR extends PHP_Token {} +class PHP_Token_LOGICAL_AND extends PHP_Token {} +class PHP_Token_PRINT extends PHP_Token {} +class PHP_Token_SR_EQUAL extends PHP_Token {} +class PHP_Token_SL_EQUAL extends PHP_Token {} +class PHP_Token_XOR_EQUAL extends PHP_Token {} +class PHP_Token_OR_EQUAL extends PHP_Token {} +class PHP_Token_AND_EQUAL extends PHP_Token {} +class PHP_Token_MOD_EQUAL extends PHP_Token {} +class PHP_Token_CONCAT_EQUAL extends PHP_Token {} +class PHP_Token_DIV_EQUAL extends PHP_Token {} +class PHP_Token_MUL_EQUAL extends PHP_Token {} +class PHP_Token_MINUS_EQUAL extends PHP_Token {} +class PHP_Token_PLUS_EQUAL extends PHP_Token {} +class PHP_Token_BOOLEAN_OR extends PHP_Token {} +class PHP_Token_BOOLEAN_AND extends PHP_Token {} +class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token {} +class PHP_Token_IS_IDENTICAL extends PHP_Token {} +class PHP_Token_IS_NOT_EQUAL extends PHP_Token {} +class PHP_Token_IS_EQUAL extends PHP_Token {} +class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token {} +class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token {} +class PHP_Token_SR extends PHP_Token {} +class PHP_Token_SL extends PHP_Token {} +class PHP_Token_INSTANCEOF extends PHP_Token {} +class PHP_Token_UNSET_CAST extends PHP_Token {} +class PHP_Token_BOOL_CAST extends PHP_Token {} +class PHP_Token_OBJECT_CAST extends PHP_Token {} +class PHP_Token_ARRAY_CAST extends PHP_Token {} +class PHP_Token_STRING_CAST extends PHP_Token {} +class PHP_Token_DOUBLE_CAST extends PHP_Token {} +class PHP_Token_INT_CAST extends PHP_Token {} +class PHP_Token_DEC extends PHP_Token {} +class PHP_Token_INC extends PHP_Token {} +class PHP_Token_CLONE extends PHP_Token {} +class PHP_Token_NEW extends PHP_Token {} +class PHP_Token_EXIT extends PHP_Token {} +class PHP_Token_IF extends PHP_Token {} +class PHP_Token_ELSEIF extends PHP_Token {} +class PHP_Token_ELSE extends PHP_Token {} +class PHP_Token_ENDIF extends PHP_Token {} +class PHP_Token_LNUMBER extends PHP_Token {} +class PHP_Token_DNUMBER extends PHP_Token {} +class PHP_Token_STRING extends PHP_Token {} +class PHP_Token_STRING_VARNAME extends PHP_Token {} +class PHP_Token_VARIABLE extends PHP_Token {} +class PHP_Token_NUM_STRING extends PHP_Token {} +class PHP_Token_INLINE_HTML extends PHP_Token {} +class PHP_Token_CHARACTER extends PHP_Token {} +class PHP_Token_BAD_CHARACTER extends PHP_Token {} +class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token {} +class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token {} +class PHP_Token_ECHO extends PHP_Token {} +class PHP_Token_DO extends PHP_Token {} +class PHP_Token_WHILE extends PHP_Token {} +class PHP_Token_ENDWHILE extends PHP_Token {} +class PHP_Token_FOR extends PHP_Token {} +class PHP_Token_ENDFOR extends PHP_Token {} +class PHP_Token_FOREACH extends PHP_Token {} +class PHP_Token_ENDFOREACH extends PHP_Token {} +class PHP_Token_DECLARE extends PHP_Token {} +class PHP_Token_ENDDECLARE extends PHP_Token {} +class PHP_Token_AS extends PHP_Token {} +class PHP_Token_SWITCH extends PHP_Token {} +class PHP_Token_ENDSWITCH extends PHP_Token {} +class PHP_Token_CASE extends PHP_Token {} +class PHP_Token_DEFAULT extends PHP_Token {} +class PHP_Token_BREAK extends PHP_Token {} +class PHP_Token_CONTINUE extends PHP_Token {} +class PHP_Token_GOTO extends PHP_Token {} +class PHP_Token_CALLABLE extends PHP_Token {} +class PHP_Token_INSTEADOF extends PHP_Token {} + +class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility +{ + protected $arguments; + protected $ccn; + protected $name; + protected $signature; + + public function getArguments() + { + if ($this->arguments !== NULL) { + return $this->arguments; + } + + $this->arguments = array(); + $i = $this->id + 3; + $tokens = $this->tokenStream->tokens(); + $typeHint = NULL; + + while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { + if ($tokens[$i] instanceof PHP_Token_STRING) { + $typeHint = (string)$tokens[$i]; + } + + else if ($tokens[$i] instanceof PHP_Token_VARIABLE) { + $this->arguments[(string)$tokens[$i]] = $typeHint; + $typeHint = NULL; + } + + $i++; + } + + return $this->arguments; + } + + public function getName() + { + if ($this->name !== NULL) { + return $this->name; + } + + $tokens = $this->tokenStream->tokens(); + + if ($tokens[$this->id+2] instanceof PHP_Token_STRING) { + $this->name = (string)$tokens[$this->id+2]; + } + + else if ($tokens[$this->id+2] instanceof PHP_Token_AMPERSAND && + $tokens[$this->id+3] instanceof PHP_Token_STRING) { + $this->name = (string)$tokens[$this->id+3]; + } + + else { + $this->name = 'anonymous function'; + } + + if ($this->name != 'anonymous function') { + for ($i = $this->id; $i; --$i) { + if ($tokens[$i] instanceof PHP_Token_NAMESPACE) { + $this->name = $tokens[$i]->getName() . '\\' . $this->name; + break; + } + + if ($tokens[$i] instanceof PHP_Token_INTERFACE) { + break; + } + } + } + + return $this->name; + } + + public function getCCN() + { + if ($this->ccn !== NULL) { + return $this->ccn; + } + + $this->ccn = 1; + $end = $this->getEndTokenId(); + $tokens = $this->tokenStream->tokens(); + + for ($i = $this->id; $i <= $end; $i++) { + switch (get_class($tokens[$i])) { + case 'PHP_Token_IF': + case 'PHP_Token_ELSEIF': + case 'PHP_Token_FOR': + case 'PHP_Token_FOREACH': + case 'PHP_Token_WHILE': + case 'PHP_Token_CASE': + case 'PHP_Token_CATCH': + case 'PHP_Token_BOOLEAN_AND': + case 'PHP_Token_LOGICAL_AND': + case 'PHP_Token_BOOLEAN_OR': + case 'PHP_Token_LOGICAL_OR': + case 'PHP_Token_QUESTION_MARK': { + $this->ccn++; + } + break; + } + } + + return $this->ccn; + } + + public function getSignature() + { + if ($this->signature !== NULL) { + return $this->signature; + } + + if ($this->getName() == 'anonymous function') { + $this->signature = 'anonymous function'; + $i = $this->id + 1; + } else { + $this->signature = ''; + $i = $this->id + 2; + } + + $tokens = $this->tokenStream->tokens(); + + while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { + $this->signature .= $tokens[$i++]; + } + + $this->signature .= ')'; + + return $this->signature; + } +} + +class PHP_Token_CONST extends PHP_Token {} +class PHP_Token_RETURN extends PHP_Token {} +class PHP_Token_TRY extends PHP_Token {} +class PHP_Token_CATCH extends PHP_Token {} +class PHP_Token_THROW extends PHP_Token {} +class PHP_Token_USE extends PHP_Token {} +class PHP_Token_GLOBAL extends PHP_Token {} +class PHP_Token_PUBLIC extends PHP_Token {} +class PHP_Token_PROTECTED extends PHP_Token {} +class PHP_Token_PRIVATE extends PHP_Token {} +class PHP_Token_FINAL extends PHP_Token {} +class PHP_Token_ABSTRACT extends PHP_Token {} +class PHP_Token_STATIC extends PHP_Token {} +class PHP_Token_VAR extends PHP_Token {} +class PHP_Token_UNSET extends PHP_Token {} +class PHP_Token_ISSET extends PHP_Token {} +class PHP_Token_EMPTY extends PHP_Token {} +class PHP_Token_HALT_COMPILER extends PHP_Token {} + +class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility +{ + protected $interfaces; + + public function getName() + { + return (string)$this->tokenStream[$this->id + 2]; + } + + public function hasParent() + { + return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS; + } + + public function getPackage() + { + $className = $this->getName(); + $docComment = $this->getDocblock(); + + $result = array( + 'namespace' => '', + 'fullPackage' => '', + 'category' => '', + 'package' => '', + 'subpackage' => '' + ); + + for ($i = $this->id; $i; --$i) { + if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) { + $result['namespace'] = $this->tokenStream[$i]->getName(); + break; + } + } + + if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['category'] = $matches[1]; + } + + if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['package'] = $matches[1]; + $result['fullPackage'] = $matches[1]; + } + + if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['subpackage'] = $matches[1]; + $result['fullPackage'] .= '.' . $matches[1]; + } + + if (empty($result['fullPackage'])) { + $result['fullPackage'] = $this->arrayToName( + explode('_', str_replace('\\', '_', $className)), '.' + ); + } + + return $result; + } + + protected function arrayToName(array $parts, $join = '\\') + { + $result = ''; + + if (count($parts) > 1) { + array_pop($parts); + + $result = join($join, $parts); + } + + return $result; + } + + public function getParent() + { + if (!$this->hasParent()) { + return FALSE; + } + + $i = $this->id + 6; + $tokens = $this->tokenStream->tokens(); + $className = (string)$tokens[$i]; + + while (isset($tokens[$i+1]) && + !$tokens[$i+1] instanceof PHP_Token_WHITESPACE) { + $className .= (string)$tokens[++$i]; + } + + return $className; + } + + public function hasInterfaces() + { + return (isset($this->tokenStream[$this->id + 4]) && + $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) || + (isset($this->tokenStream[$this->id + 8]) && + $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS); + } + + public function getInterfaces() + { + if ($this->interfaces !== NULL) { + return $this->interfaces; + } + + if (!$this->hasInterfaces()) { + return ($this->interfaces = FALSE); + } + + if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) { + $i = $this->id + 3; + } else { + $i = $this->id + 7; + } + + $tokens = $this->tokenStream->tokens(); + + while (!$tokens[$i+1] instanceof PHP_Token_OPEN_CURLY) { + $i++; + + if ($tokens[$i] instanceof PHP_Token_STRING) { + $this->interfaces[] = (string)$tokens[$i]; + } + } + + return $this->interfaces; + } +} + +class PHP_Token_CLASS extends PHP_Token_INTERFACE {} +class PHP_Token_TRAIT extends PHP_Token_INTERFACE {} +class PHP_Token_EXTENDS extends PHP_Token {} +class PHP_Token_IMPLEMENTS extends PHP_Token {} +class PHP_Token_OBJECT_OPERATOR extends PHP_Token {} +class PHP_Token_DOUBLE_ARROW extends PHP_Token {} +class PHP_Token_LIST extends PHP_Token {} +class PHP_Token_ARRAY extends PHP_Token {} +class PHP_Token_CLASS_C extends PHP_Token {} +class PHP_Token_TRAIT_C extends PHP_Token {} +class PHP_Token_METHOD_C extends PHP_Token {} +class PHP_Token_FUNC_C extends PHP_Token {} +class PHP_Token_LINE extends PHP_Token {} +class PHP_Token_FILE extends PHP_Token {} +class PHP_Token_COMMENT extends PHP_Token {} +class PHP_Token_DOC_COMMENT extends PHP_Token {} +class PHP_Token_OPEN_TAG extends PHP_Token {} +class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token {} +class PHP_Token_CLOSE_TAG extends PHP_Token {} +class PHP_Token_WHITESPACE extends PHP_Token {} +class PHP_Token_START_HEREDOC extends PHP_Token {} +class PHP_Token_END_HEREDOC extends PHP_Token {} +class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token {} +class PHP_Token_CURLY_OPEN extends PHP_Token {} +class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token {} + +class PHP_Token_NAMESPACE extends PHP_TokenWithScope +{ + public function getName() + { + $tokens = $this->tokenStream->tokens(); + $namespace = (string)$tokens[$this->id+2]; + + for ($i = $this->id + 3; ; $i += 2) { + if (isset($tokens[$i]) && + $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) { + $namespace .= '\\' . $tokens[$i+1]; + } else { + break; + } + } + + return $namespace; + } +} + +class PHP_Token_NS_C extends PHP_Token {} +class PHP_Token_DIR extends PHP_Token {} +class PHP_Token_NS_SEPARATOR extends PHP_Token {} +class PHP_Token_DOUBLE_COLON extends PHP_Token {} +class PHP_Token_OPEN_BRACKET extends PHP_Token {} +class PHP_Token_CLOSE_BRACKET extends PHP_Token {} +class PHP_Token_OPEN_SQUARE extends PHP_Token {} +class PHP_Token_CLOSE_SQUARE extends PHP_Token {} +class PHP_Token_OPEN_CURLY extends PHP_Token {} +class PHP_Token_CLOSE_CURLY extends PHP_Token {} +class PHP_Token_SEMICOLON extends PHP_Token {} +class PHP_Token_DOT extends PHP_Token {} +class PHP_Token_COMMA extends PHP_Token {} +class PHP_Token_EQUAL extends PHP_Token {} +class PHP_Token_LT extends PHP_Token {} +class PHP_Token_GT extends PHP_Token {} +class PHP_Token_PLUS extends PHP_Token {} +class PHP_Token_MINUS extends PHP_Token {} +class PHP_Token_MULT extends PHP_Token {} +class PHP_Token_DIV extends PHP_Token {} +class PHP_Token_QUESTION_MARK extends PHP_Token {} +class PHP_Token_EXCLAMATION_MARK extends PHP_Token {} +class PHP_Token_COLON extends PHP_Token {} +class PHP_Token_DOUBLE_QUOTES extends PHP_Token {} +class PHP_Token_AT extends PHP_Token {} +class PHP_Token_AMPERSAND extends PHP_Token {} +class PHP_Token_PERCENT extends PHP_Token {} +class PHP_Token_PIPE extends PHP_Token {} +class PHP_Token_DOLLAR extends PHP_Token {} +class PHP_Token_CARET extends PHP_Token {} +class PHP_Token_TILDE extends PHP_Token {} +class PHP_Token_BACKTICK extends PHP_Token {} diff --git a/vendor/phpunit/php-token-stream/PHP/Token/Stream.php b/vendor/phpunit/php-token-stream/PHP/Token/Stream.php new file mode 100644 index 0000000..77ba0dc --- /dev/null +++ b/vendor/phpunit/php-token-stream/PHP/Token/Stream.php @@ -0,0 +1,568 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +/** + * A stream of PHP tokens. + * + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/tree + * @since Class available since Release 1.0.0 + */ +class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator +{ + /** + * @var array + */ + protected static $customTokens = array( + '(' => 'PHP_Token_OPEN_BRACKET', + ')' => 'PHP_Token_CLOSE_BRACKET', + '[' => 'PHP_Token_OPEN_SQUARE', + ']' => 'PHP_Token_CLOSE_SQUARE', + '{' => 'PHP_Token_OPEN_CURLY', + '}' => 'PHP_Token_CLOSE_CURLY', + ';' => 'PHP_Token_SEMICOLON', + '.' => 'PHP_Token_DOT', + ',' => 'PHP_Token_COMMA', + '=' => 'PHP_Token_EQUAL', + '<' => 'PHP_Token_LT', + '>' => 'PHP_Token_GT', + '+' => 'PHP_Token_PLUS', + '-' => 'PHP_Token_MINUS', + '*' => 'PHP_Token_MULT', + '/' => 'PHP_Token_DIV', + '?' => 'PHP_Token_QUESTION_MARK', + '!' => 'PHP_Token_EXCLAMATION_MARK', + ':' => 'PHP_Token_COLON', + '"' => 'PHP_Token_DOUBLE_QUOTES', + '@' => 'PHP_Token_AT', + '&' => 'PHP_Token_AMPERSAND', + '%' => 'PHP_Token_PERCENT', + '|' => 'PHP_Token_PIPE', + '$' => 'PHP_Token_DOLLAR', + '^' => 'PHP_Token_CARET', + '~' => 'PHP_Token_TILDE', + '`' => 'PHP_Token_BACKTICK' + ); + + /** + * @var string + */ + protected $filename; + + /** + * @var array + */ + protected $tokens = array(); + + /** + * @var integer + */ + protected $position = 0; + + /** + * @var array + */ + protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0); + + /** + * @var array + */ + protected $classes; + + /** + * @var array + */ + protected $functions; + + /** + * @var array + */ + protected $includes; + + /** + * @var array + */ + protected $interfaces; + + /** + * @var array + */ + protected $traits; + + /** + * Constructor. + * + * @param string $sourceCode + */ + public function __construct($sourceCode) + { + if (is_file($sourceCode)) { + $this->filename = $sourceCode; + $sourceCode = file_get_contents($sourceCode); + } + + $this->scan($sourceCode); + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->tokens = array(); + } + + /** + * @return string + */ + public function __toString() + { + $buffer = ''; + + foreach ($this as $token) { + $buffer .= $token; + } + + return $buffer; + } + + /** + * @return string + * @since Method available since Release 1.1.0 + */ + public function getFilename() + { + return $this->filename; + } + + /** + * Scans the source for sequences of characters and converts them into a + * stream of tokens. + * + * @param string $sourceCode + */ + protected function scan($sourceCode) + { + $line = 1; + $tokens = token_get_all($sourceCode); + $numTokens = count($tokens); + + for ($i = 0; $i < $numTokens; ++$i) { + $token = $tokens[$i]; + unset($tokens[$i]); + + if (is_array($token)) { + $text = $token[1]; + $tokenClass = 'PHP_Token_' . substr(token_name($token[0]), 2); + } else { + $text = $token; + $tokenClass = self::$customTokens[$token]; + } + + $this->tokens[] = new $tokenClass($text, $line, $this, $i); + $lines = substr_count($text, "\n"); + $line += $lines; + + if ($tokenClass == 'PHP_Token_HALT_COMPILER') { + break; + } + + else if ($tokenClass == 'PHP_Token_COMMENT' || + $tokenClass == 'PHP_Token_DOC_COMMENT') { + $this->linesOfCode['cloc'] += $lines + 1; + } + } + + $this->linesOfCode['loc'] = substr_count($sourceCode, "\n"); + $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - + $this->linesOfCode['cloc']; + } + + /** + * @return integer + */ + public function count() + { + return count($this->tokens); + } + + /** + * @return PHP_Token[] + */ + public function tokens() + { + return $this->tokens; + } + + /** + * @return array + */ + public function getClasses() + { + if ($this->classes !== NULL) { + return $this->classes; + } + + $this->parse(); + + return $this->classes; + } + + /** + * @return array + */ + public function getFunctions() + { + if ($this->functions !== NULL) { + return $this->functions; + } + + $this->parse(); + + return $this->functions; + } + + /** + * @return array + */ + public function getInterfaces() + { + if ($this->interfaces !== NULL) { + return $this->interfaces; + } + + $this->parse(); + + return $this->interfaces; + } + + /** + * @return array + * @since Method available since Release 1.1.0 + */ + public function getTraits() + { + if ($this->traits !== NULL) { + return $this->traits; + } + + $this->parse(); + + return $this->traits; + } + + /** + * Gets the names of all files that have been included + * using include(), include_once(), require() or require_once(). + * + * Parameter $categorize set to TRUE causing this function to return a + * multi-dimensional array with categories in the keys of the first dimension + * and constants and their values in the second dimension. + * + * Parameter $category allow to filter following specific inclusion type + * + * @param bool $categorize OPTIONAL + * @param string $category OPTIONAL Either 'require_once', 'require', + * 'include_once', 'include'. + * @return array + * @since Method available since Release 1.1.0 + */ + public function getIncludes($categorize = FALSE, $category = NULL) + { + if ($this->includes === NULL) { + $this->includes = array( + 'require_once' => array(), + 'require' => array(), + 'include_once' => array(), + 'include' => array() + ); + + foreach ($this->tokens as $token) { + switch (get_class($token)) { + case 'PHP_Token_REQUIRE_ONCE': + case 'PHP_Token_REQUIRE': + case 'PHP_Token_INCLUDE_ONCE': + case 'PHP_Token_INCLUDE': { + $this->includes[$token->getType()][] = $token->getName(); + } + break; + } + } + } + + if (isset($this->includes[$category])) { + $includes = $this->includes[$category]; + } + + else if ($categorize === FALSE) { + $includes = array_merge( + $this->includes['require_once'], + $this->includes['require'], + $this->includes['include_once'], + $this->includes['include'] + ); + } else { + $includes = $this->includes; + } + + return $includes; + } + + protected function parse() + { + $this->interfaces = array(); + $this->classes = array(); + $this->traits = array(); + $this->functions = array(); + $class = FALSE; + $classEndLine = FALSE; + $trait = FALSE; + $traitEndLine = FALSE; + $interface = FALSE; + $interfaceEndLine = FALSE; + + foreach ($this->tokens as $token) { + switch (get_class($token)) { + case 'PHP_Token_HALT_COMPILER': { + return; + } + break; + + case 'PHP_Token_INTERFACE': { + $interface = $token->getName(); + $interfaceEndLine = $token->getEndLine(); + + $this->interfaces[$interface] = array( + 'methods' => array(), + 'parent' => $token->getParent(), + 'keywords' => $token->getKeywords(), + 'docblock' => $token->getDocblock(), + 'startLine' => $token->getLine(), + 'endLine' => $interfaceEndLine, + 'package' => $token->getPackage(), + 'file' => $this->filename + ); + } + break; + + case 'PHP_Token_CLASS': + case 'PHP_Token_TRAIT': { + $tmp = array( + 'methods' => array(), + 'parent' => $token->getParent(), + 'interfaces'=> $token->getInterfaces(), + 'keywords' => $token->getKeywords(), + 'docblock' => $token->getDocblock(), + 'startLine' => $token->getLine(), + 'endLine' => $token->getEndLine(), + 'package' => $token->getPackage(), + 'file' => $this->filename + ); + + if ($token instanceof PHP_Token_CLASS) { + $class = $token->getName(); + $classEndLine = $token->getEndLine(); + $this->classes[$class] = $tmp; + } else { + $trait = $token->getName(); + $traitEndLine = $token->getEndLine(); + $this->traits[$trait] = $tmp; + } + } + break; + + case 'PHP_Token_FUNCTION': { + $name = $token->getName(); + $tmp = array( + 'docblock' => $token->getDocblock(), + 'keywords' => $token->getKeywords(), + 'visibility'=> $token->getVisibility(), + 'signature' => $token->getSignature(), + 'startLine' => $token->getLine(), + 'endLine' => $token->getEndLine(), + 'ccn' => $token->getCCN(), + 'file' => $this->filename + ); + + if ($class === FALSE && + $trait === FALSE && + $interface === FALSE) { + $this->functions[$name] = $tmp; + } + + else if ($class !== FALSE) { + $this->classes[$class]['methods'][$name] = $tmp; + } + + else if ($trait !== FALSE) { + $this->traits[$trait]['methods'][$name] = $tmp; + } + + else { + $this->interfaces[$interface]['methods'][$name] = $tmp; + } + } + break; + + case 'PHP_Token_CLOSE_CURLY': { + if ($classEndLine !== FALSE && + $classEndLine == $token->getLine()) { + $class = FALSE; + $classEndLine = FALSE; + } + + else if ($traitEndLine !== FALSE && + $traitEndLine == $token->getLine()) { + $trait = FALSE; + $traitEndLine = FALSE; + } + + else if ($interfaceEndLine !== FALSE && + $interfaceEndLine == $token->getLine()) { + $interface = FALSE; + $interfaceEndLine = FALSE; + } + } + break; + } + } + } + + /** + * @return array + */ + public function getLinesOfCode() + { + return $this->linesOfCode; + } + + /** + */ + public function rewind() + { + $this->position = 0; + } + + /** + * @return boolean + */ + public function valid() + { + return isset($this->tokens[$this->position]); + } + + /** + * @return integer + */ + public function key() + { + return $this->position; + } + + /** + * @return PHP_Token + */ + public function current() + { + return $this->tokens[$this->position]; + } + + /** + */ + public function next() + { + $this->position++; + } + + /** + * @param mixed $offset + */ + public function offsetExists($offset) + { + return isset($this->tokens[$offset]); + } + + /** + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->tokens[$offset]; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->tokens[$offset] = $value; + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) + { + unset($this->tokens[$offset]); + } + + /** + * Seek to an absolute position. + * + * @param integer $position + * @throws OutOfBoundsException + */ + public function seek($position) + { + $this->position = $position; + + if (!$this->valid()) { + throw new OutOfBoundsException('Invalid seek position'); + } + } +} diff --git a/vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php b/vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php new file mode 100644 index 0000000..9895b34 --- /dev/null +++ b/vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php @@ -0,0 +1,226 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-token-stream/tree + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL;; + + if ($classes === NULL) { + $classes = array( + 'php_token' => '/Token.php', + 'php_token_abstract' => '/Token.php', + 'php_token_ampersand' => '/Token.php', + 'php_token_and_equal' => '/Token.php', + 'php_token_array' => '/Token.php', + 'php_token_array_cast' => '/Token.php', + 'php_token_as' => '/Token.php', + 'php_token_at' => '/Token.php', + 'php_token_backtick' => '/Token.php', + 'php_token_bad_character' => '/Token.php', + 'php_token_bool_cast' => '/Token.php', + 'php_token_boolean_and' => '/Token.php', + 'php_token_boolean_or' => '/Token.php', + 'php_token_break' => '/Token.php', + 'php_token_callable' => '/Token.php', + 'php_token_caret' => '/Token.php', + 'php_token_case' => '/Token.php', + 'php_token_catch' => '/Token.php', + 'php_token_character' => '/Token.php', + 'php_token_class' => '/Token.php', + 'php_token_class_c' => '/Token.php', + 'php_token_clone' => '/Token.php', + 'php_token_close_bracket' => '/Token.php', + 'php_token_close_curly' => '/Token.php', + 'php_token_close_square' => '/Token.php', + 'php_token_close_tag' => '/Token.php', + 'php_token_colon' => '/Token.php', + 'php_token_comma' => '/Token.php', + 'php_token_comment' => '/Token.php', + 'php_token_concat_equal' => '/Token.php', + 'php_token_const' => '/Token.php', + 'php_token_constant_encapsed_string' => '/Token.php', + 'php_token_continue' => '/Token.php', + 'php_token_curly_open' => '/Token.php', + 'php_token_dec' => '/Token.php', + 'php_token_declare' => '/Token.php', + 'php_token_default' => '/Token.php', + 'php_token_dir' => '/Token.php', + 'php_token_div' => '/Token.php', + 'php_token_div_equal' => '/Token.php', + 'php_token_dnumber' => '/Token.php', + 'php_token_do' => '/Token.php', + 'php_token_doc_comment' => '/Token.php', + 'php_token_dollar' => '/Token.php', + 'php_token_dollar_open_curly_braces' => '/Token.php', + 'php_token_dot' => '/Token.php', + 'php_token_double_arrow' => '/Token.php', + 'php_token_double_cast' => '/Token.php', + 'php_token_double_colon' => '/Token.php', + 'php_token_double_quotes' => '/Token.php', + 'php_token_echo' => '/Token.php', + 'php_token_else' => '/Token.php', + 'php_token_elseif' => '/Token.php', + 'php_token_empty' => '/Token.php', + 'php_token_encapsed_and_whitespace' => '/Token.php', + 'php_token_end_heredoc' => '/Token.php', + 'php_token_enddeclare' => '/Token.php', + 'php_token_endfor' => '/Token.php', + 'php_token_endforeach' => '/Token.php', + 'php_token_endif' => '/Token.php', + 'php_token_endswitch' => '/Token.php', + 'php_token_endwhile' => '/Token.php', + 'php_token_equal' => '/Token.php', + 'php_token_eval' => '/Token.php', + 'php_token_exclamation_mark' => '/Token.php', + 'php_token_exit' => '/Token.php', + 'php_token_extends' => '/Token.php', + 'php_token_file' => '/Token.php', + 'php_token_final' => '/Token.php', + 'php_token_for' => '/Token.php', + 'php_token_foreach' => '/Token.php', + 'php_token_func_c' => '/Token.php', + 'php_token_function' => '/Token.php', + 'php_token_global' => '/Token.php', + 'php_token_goto' => '/Token.php', + 'php_token_gt' => '/Token.php', + 'php_token_halt_compiler' => '/Token.php', + 'php_token_if' => '/Token.php', + 'php_token_implements' => '/Token.php', + 'php_token_inc' => '/Token.php', + 'php_token_include' => '/Token.php', + 'php_token_include_once' => '/Token.php', + 'php_token_includes' => '/Token.php', + 'php_token_inline_html' => '/Token.php', + 'php_token_instanceof' => '/Token.php', + 'php_token_insteadof' => '/Token.php', + 'php_token_int_cast' => '/Token.php', + 'php_token_interface' => '/Token.php', + 'php_token_is_equal' => '/Token.php', + 'php_token_is_greater_or_equal' => '/Token.php', + 'php_token_is_identical' => '/Token.php', + 'php_token_is_not_equal' => '/Token.php', + 'php_token_is_not_identical' => '/Token.php', + 'php_token_is_smaller_or_equal' => '/Token.php', + 'php_token_isset' => '/Token.php', + 'php_token_line' => '/Token.php', + 'php_token_list' => '/Token.php', + 'php_token_lnumber' => '/Token.php', + 'php_token_logical_and' => '/Token.php', + 'php_token_logical_or' => '/Token.php', + 'php_token_logical_xor' => '/Token.php', + 'php_token_lt' => '/Token.php', + 'php_token_method_c' => '/Token.php', + 'php_token_minus' => '/Token.php', + 'php_token_minus_equal' => '/Token.php', + 'php_token_mod_equal' => '/Token.php', + 'php_token_mul_equal' => '/Token.php', + 'php_token_mult' => '/Token.php', + 'php_token_namespace' => '/Token.php', + 'php_token_new' => '/Token.php', + 'php_token_ns_c' => '/Token.php', + 'php_token_ns_separator' => '/Token.php', + 'php_token_num_string' => '/Token.php', + 'php_token_object_cast' => '/Token.php', + 'php_token_object_operator' => '/Token.php', + 'php_token_open_bracket' => '/Token.php', + 'php_token_open_curly' => '/Token.php', + 'php_token_open_square' => '/Token.php', + 'php_token_open_tag' => '/Token.php', + 'php_token_open_tag_with_echo' => '/Token.php', + 'php_token_or_equal' => '/Token.php', + 'php_token_paamayim_nekudotayim' => '/Token.php', + 'php_token_percent' => '/Token.php', + 'php_token_pipe' => '/Token.php', + 'php_token_plus' => '/Token.php', + 'php_token_plus_equal' => '/Token.php', + 'php_token_print' => '/Token.php', + 'php_token_private' => '/Token.php', + 'php_token_protected' => '/Token.php', + 'php_token_public' => '/Token.php', + 'php_token_question_mark' => '/Token.php', + 'php_token_require' => '/Token.php', + 'php_token_require_once' => '/Token.php', + 'php_token_return' => '/Token.php', + 'php_token_semicolon' => '/Token.php', + 'php_token_sl' => '/Token.php', + 'php_token_sl_equal' => '/Token.php', + 'php_token_sr' => '/Token.php', + 'php_token_sr_equal' => '/Token.php', + 'php_token_start_heredoc' => '/Token.php', + 'php_token_static' => '/Token.php', + 'php_token_stream' => '/Token/Stream.php', + 'php_token_stream_cachingfactory' => '/Token/Stream/CachingFactory.php', + 'php_token_string' => '/Token.php', + 'php_token_string_cast' => '/Token.php', + 'php_token_string_varname' => '/Token.php', + 'php_token_switch' => '/Token.php', + 'php_token_throw' => '/Token.php', + 'php_token_tilde' => '/Token.php', + 'php_token_trait' => '/Token.php', + 'php_token_trait_c' => '/Token.php', + 'php_token_try' => '/Token.php', + 'php_token_unset' => '/Token.php', + 'php_token_unset_cast' => '/Token.php', + 'php_token_use' => '/Token.php', + 'php_token_var' => '/Token.php', + 'php_token_variable' => '/Token.php', + 'php_token_while' => '/Token.php', + 'php_token_whitespace' => '/Token.php', + 'php_token_xor_equal' => '/Token.php', + 'php_tokenwithscope' => '/Token.php', + 'php_tokenwithscopeandvisibility' => '/Token.php' + ); + + $path = dirname(dirname(dirname(__FILE__))); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php.in b/vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php.in new file mode 100644 index 0000000..ebd488b --- /dev/null +++ b/vendor/phpunit/php-token-stream/PHP/Token/Stream/Autoload.php.in @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/php-token-stream/tree + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL;; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(dirname(__FILE__))); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/php-token-stream/PHP/Token/Stream/CachingFactory.php b/vendor/phpunit/php-token-stream/PHP/Token/Stream/CachingFactory.php new file mode 100644 index 0000000..6ff2c5d --- /dev/null +++ b/vendor/phpunit/php-token-stream/PHP/Token/Stream/CachingFactory.php @@ -0,0 +1,85 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +/** + * A caching factory for token stream objects. + * + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/tree + * @since Class available since Release 1.0.0 + */ +class PHP_Token_Stream_CachingFactory +{ + /** + * @var array + */ + protected static $cache = array(); + + /** + * @param string $filename + * @return PHP_Token_Stream + */ + public static function get($filename) + { + if (!isset(self::$cache[$filename])) { + self::$cache[$filename] = new PHP_Token_Stream($filename); + } + + return self::$cache[$filename]; + } + + /** + * @param string $filename + */ + public static function clear($filename = NULL) + { + if (is_string($filename)) { + unset(self::$cache[$filename]); + } else { + self::$cache = array(); + } + } +} diff --git a/vendor/phpunit/php-token-stream/README.markdown b/vendor/phpunit/php-token-stream/README.markdown new file mode 100644 index 0000000..e233f41 --- /dev/null +++ b/vendor/phpunit/php-token-stream/README.markdown @@ -0,0 +1,23 @@ +PHP_TokenStream +=============== + +Installation +------------ + +PHP_TokenStream should be installed using the [PEAR Installer](http://pear.php.net/). This installer is the backbone of PEAR, which provides a distribution system for PHP packages, and is shipped with every release of PHP since version 4.3.0. + +The PEAR channel (`pear.phpunit.de`) that is used to distribute PHP_TokenStream needs to be registered with the local PEAR environment: + + sb@ubuntu ~ % pear channel-discover pear.phpunit.de + Adding Channel "pear.phpunit.de" succeeded + Discovery of channel "pear.phpunit.de" succeeded + +This has to be done only once. Now the PEAR Installer can be used to install packages from the PHPUnit channel: + + sb@ubuntu tokenstream % pear install phpunit/PHP_TokenStream-beta + downloading PHP_TokenStream-0.9.1.tgz ... + Starting to download PHP_TokenStream-0.9.1.tgz (5,113 bytes) + ...done: 5,113 bytes + install ok: channel://pear.phpunit.de/PHP_TokenStream-0.9.1 + +After the installation you can find the PHP_TokenStream source files inside your local PEAR directory; the path is usually `/usr/lib/php/PHP`. diff --git a/vendor/phpunit/php-token-stream/Tests/Token/ClassTest.php b/vendor/phpunit/php-token-stream/Tests/Token/ClassTest.php new file mode 100644 index 0000000..5dd6e99 --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/Token/ClassTest.php @@ -0,0 +1,122 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Laurent Laville + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.2 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once 'PHP/Token/Stream.php'; + +/** + * Tests for the PHP_Token_CLASS class. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Laurent Laville + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/ + * @since Class available since Release 1.0.2 + */ +class PHP_Token_ClassTest extends PHPUnit_Framework_TestCase +{ + protected $class; + protected $function; + + protected function setUp() + { + $ts = new PHP_Token_Stream(TEST_FILES_PATH . 'source2.php'); + + foreach ($ts as $token) { + if ($token instanceof PHP_Token_CLASS) { + $this->class = $token; + } + + if ($token instanceof PHP_Token_FUNCTION) { + $this->function = $token; + break; + } + } + } + + /** + * @covers PHP_Token_CLASS::getKeywords + */ + public function testGetClassKeywords() + { + $this->assertEquals('abstract', $this->class->getKeywords()); + } + + /** + * @covers PHP_Token_FUNCTION::getKeywords + */ + public function testGetFunctionKeywords() + { + $this->assertEquals('abstract,static', $this->function->getKeywords()); + } + + /** + * @covers PHP_Token_FUNCTION::getVisibility + */ + public function testGetFunctionVisibility() + { + $this->assertEquals('public', $this->function->getVisibility()); + } + + public function testIssue19() + { + $ts = new PHP_Token_Stream(TEST_FILES_PATH . 'issue19.php'); + + foreach ($ts as $token) { + if ($token instanceof PHP_Token_CLASS) { + $this->assertFalse($token->hasInterfaces()); + } + } + } +} diff --git a/vendor/phpunit/php-token-stream/Tests/Token/FunctionTest.php b/vendor/phpunit/php-token-stream/Tests/Token/FunctionTest.php new file mode 100644 index 0000000..d57162b --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/Token/FunctionTest.php @@ -0,0 +1,160 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once 'PHP/Token/Stream.php'; + +/** + * Tests for the PHP_Token_FUNCTION class. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/ + * @since Class available since Release 1.0.0 + */ +class PHP_Token_FunctionTest extends PHPUnit_Framework_TestCase +{ + protected $functions; + + protected function setUp() + { + $ts = new PHP_Token_Stream(TEST_FILES_PATH . 'source.php'); + + foreach ($ts as $token) { + if ($token instanceof PHP_Token_FUNCTION) { + $this->functions[] = $token; + } + } + } + + /** + * @covers PHP_Token_FUNCTION::getArguments + */ + public function testGetArguments() + { + $this->assertEquals(array(), $this->functions[0]->getArguments()); + + $this->assertEquals( + array('$baz' => 'Baz'), $this->functions[1]->getArguments() + ); + + $this->assertEquals( + array('$foobar' => 'Foobar'), $this->functions[2]->getArguments() + ); + + $this->assertEquals( + array('$barfoo' => 'Barfoo'), $this->functions[3]->getArguments() + ); + + $this->assertEquals(array(), $this->functions[4]->getArguments()); + } + + /** + * @covers PHP_Token_FUNCTION::getName + */ + public function testGetName() + { + $this->assertEquals('foo', $this->functions[0]->getName()); + $this->assertEquals('bar', $this->functions[1]->getName()); + $this->assertEquals('foobar', $this->functions[2]->getName()); + $this->assertEquals('barfoo', $this->functions[3]->getName()); + $this->assertEquals('baz', $this->functions[4]->getName()); + } + + /** + * @covers PHP_Token::getLine + */ + public function testGetLine() + { + $this->assertEquals(5, $this->functions[0]->getLine()); + $this->assertEquals(10, $this->functions[1]->getLine()); + $this->assertEquals(17, $this->functions[2]->getLine()); + $this->assertEquals(21, $this->functions[3]->getLine()); + $this->assertEquals(29, $this->functions[4]->getLine()); + } + + /** + * @covers PHP_TokenWithScope::getEndLine + */ + public function testGetEndLine() + { + $this->assertEquals(5, $this->functions[0]->getEndLine()); + $this->assertEquals(12, $this->functions[1]->getEndLine()); + $this->assertEquals(19, $this->functions[2]->getEndLine()); + $this->assertEquals(23, $this->functions[3]->getEndLine()); + $this->assertEquals(31, $this->functions[4]->getEndLine()); + } + + /** + * @covers PHP_Token_FUNCTION::getDocblock + */ + public function testGetDocblock() + { + $this->assertNull($this->functions[0]->getDocblock()); + + $this->assertEquals( + "/**\n * @param Baz \$baz\n */", + $this->functions[1]->getDocblock() + ); + + $this->assertEquals( + "/**\n * @param Foobar \$foobar\n */", + $this->functions[2]->getDocblock() + ); + + $this->assertNull($this->functions[3]->getDocblock()); + $this->assertNull($this->functions[4]->getDocblock()); + } +} diff --git a/vendor/phpunit/php-token-stream/Tests/Token/IncludeTest.php b/vendor/phpunit/php-token-stream/Tests/Token/IncludeTest.php new file mode 100644 index 0000000..710be22 --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/Token/IncludeTest.php @@ -0,0 +1,117 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Laurent Laville + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.2 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once 'PHP/Token/Stream.php'; + +/** + * Tests for the PHP_Token_REQUIRE_ONCE, PHP_Token_REQUIRE + * PHP_Token_INCLUDE_ONCE and PHP_Token_INCLUDE_ONCE classes. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Laurent Laville + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/ + * @since Class available since Release 1.0.2 + */ +class PHP_Token_IncludeTest extends PHPUnit_Framework_TestCase +{ + protected $ts; + + protected function setUp() + { + $this->ts = new PHP_Token_Stream(TEST_FILES_PATH . 'source3.php'); + } + + /** + * @covers PHP_Token_Includes::getName + * @covers PHP_Token_Includes::getType + */ + public function testGetIncludes() + { + $this->assertSame( + array('test4.php', 'test3.php', 'test2.php', 'test1.php'), + $this->ts->getIncludes() + ); + } + + /** + * @covers PHP_Token_Includes::getName + * @covers PHP_Token_Includes::getType + */ + public function testGetIncludesCategorized() + { + $this->assertSame( + array( + 'require_once' => array('test4.php'), + 'require' => array('test3.php'), + 'include_once' => array('test2.php'), + 'include' => array('test1.php') + ), + $this->ts->getIncludes(TRUE) + ); + } + + /** + * @covers PHP_Token_Includes::getName + * @covers PHP_Token_Includes::getType + */ + public function testGetIncludesCategory() + { + $this->assertSame( + array('test4.php'), + $this->ts->getIncludes(TRUE, 'require_once') + ); + } +} diff --git a/vendor/phpunit/php-token-stream/Tests/Token/InterfaceTest.php b/vendor/phpunit/php-token-stream/Tests/Token/InterfaceTest.php new file mode 100644 index 0000000..1c689bf --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/Token/InterfaceTest.php @@ -0,0 +1,236 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @author Laurent Laville + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once 'PHP/Token/Stream.php'; + +/** + * Tests for the PHP_Token_INTERFACE class. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @author Laurent Laville + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/ + * @since Class available since Release 1.0.0 + */ +class PHP_Token_InterfaceTest extends PHPUnit_Framework_TestCase +{ + protected $class; + protected $interfaces; + + protected function setUp() + { + $ts = new PHP_Token_Stream(TEST_FILES_PATH . 'source4.php'); + $i = 0; + foreach ($ts as $token) { + if ($token instanceof PHP_Token_CLASS) { + $this->class = $token; + } + elseif ($token instanceof PHP_Token_INTERFACE) { + $this->interfaces[$i] = $token; + $i++; + } + } + } + + /** + * @covers PHP_Token_INTERFACE::getName + */ + public function testGetName() + { + $this->assertEquals( + 'iTemplate', $this->interfaces[0]->getName() + ); + } + + /** + * @covers PHP_Token_INTERFACE::getParent + */ + public function testGetParentNotExists() + { + $this->assertFalse( + $this->interfaces[0]->getParent() + ); + } + + /** + * @covers PHP_Token_INTERFACE::hasParent + */ + public function testHasParentNotExists() + { + $this->assertFalse( + $this->interfaces[0]->hasParent() + ); + } + + /** + * @covers PHP_Token_INTERFACE::getParent + */ + public function testGetParentExists() + { + $this->assertEquals( + 'a', $this->interfaces[2]->getParent() + ); + } + + /** + * @covers PHP_Token_INTERFACE::hasParent + */ + public function testHasParentExists() + { + $this->assertTrue( + $this->interfaces[2]->hasParent() + ); + } + + /** + * @covers PHP_Token_INTERFACE::getInterfaces + */ + public function testGetInterfacesExists() + { + $this->assertEquals( + array('b'), + $this->class->getInterfaces() + ); + } + + /** + * @covers PHP_Token_INTERFACE::hasInterfaces + */ + public function testHasInterfacesExists() + { + $this->assertTrue( + $this->class->hasInterfaces() + ); + } + /** + * @covers PHP_Token_INTERFACE::getPackage + */ + public function testGetPackageNamespace() { + $tokenStream = new PHP_Token_Stream(TEST_FILES_PATH . 'classInNamespace.php'); + foreach($tokenStream as $token) { + if($token instanceOf PHP_Token_INTERFACE) { + $package = $token->getPackage(); + $this->assertSame('Foo\\Bar', $package['namespace']); + } + } + } + + + public function provideFilesWithClassesWithinMultipleNamespaces() { + return array( + array(TEST_FILES_PATH . 'multipleNamespacesWithOneClassUsingBraces.php'), + array(TEST_FILES_PATH . 'multipleNamespacesWithOneClassUsingNonBraceSyntax.php'), + ); + } + + /** + * @dataProvider provideFilesWithClassesWithinMultipleNamespaces + * @covers PHP_Token_INTERFACE::getPackage + */ + public function testGetPackageNamespaceForFileWithMultipleNamespaces($filepath) { + $tokenStream = new PHP_Token_Stream($filepath); + $firstClassFound = false; + foreach($tokenStream as $token) { + if($firstClassFound === false && $token instanceOf PHP_Token_INTERFACE) { + $package = $token->getPackage(); + $this->assertSame('TestClassInBar', $token->getName()); + $this->assertSame('Foo\\Bar', $package['namespace']); + $firstClassFound = true; + continue; + } + // Secound class + if($token instanceOf PHP_Token_INTERFACE) { + $package = $token->getPackage(); + $this->assertSame('TestClassInBaz', $token->getName()); + $this->assertSame('Foo\\Baz', $package['namespace']); + return; + } + } + $this->fail("Seachring for 2 classes failed"); + } + + public function testGetPackageNamespaceIsEmptyForInterfacesThatAreNotWithinNamespaces() { + foreach($this->interfaces as $token) { + $package = $token->getPackage(); + $this->assertSame("", $package['namespace']); + } + } + + /** + * @covers PHP_Token_INTERFACE::getPackage + */ + public function testGetPackageNamespaceWhenExtentingFromNamespaceClass() { + $tokenStream = new PHP_Token_Stream(TEST_FILES_PATH . 'classExtendsNamespacedClass.php'); + $firstClassFound = false; + foreach($tokenStream as $token) { + if($firstClassFound === false && $token instanceOf PHP_Token_INTERFACE) { + $package = $token->getPackage(); + $this->assertSame('Baz', $token->getName()); + $this->assertSame('Foo\\Bar', $package['namespace']); + $firstClassFound = true; + continue; + } + if($token instanceOf PHP_Token_INTERFACE) { + $package = $token->getPackage(); + $this->assertSame('Extender', $token->getName()); + $this->assertSame('Other\\Space', $package['namespace']); + return; + } + } + $this->fail("Searching for 2 classes failed"); + } +} diff --git a/vendor/phpunit/php-token-stream/Tests/Token/NamespaceTest.php b/vendor/phpunit/php-token-stream/Tests/Token/NamespaceTest.php new file mode 100644 index 0000000..bc0dbcb --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/Token/NamespaceTest.php @@ -0,0 +1,124 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . + '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once 'PHP/Token/Stream.php'; + +/** + * Tests for the PHP_Token_NAMESPACE class. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/ + * @since Class available since Release 1.0.0 + */ +class PHP_Token_NamespaceTest extends PHPUnit_Framework_TestCase +{ + /** + * @covers PHP_Token_NAMESPACE::getName + */ + public function testGetName() + { + $tokenStream = new PHP_Token_Stream( + TEST_FILES_PATH . 'classInNamespace.php' + ); + + foreach ($tokenStream as $token) { + if ($token instanceof PHP_Token_NAMESPACE) { + $this->assertSame('Foo\\Bar', $token->getName()); + } + } + } + + public function testGetStartLineWithUnscopedNamespace() + { + $tokenStream = new PHP_Token_Stream(TEST_FILES_PATH . 'classInNamespace.php'); + foreach($tokenStream as $token) { + if($token instanceOf PHP_Token_NAMESPACE) { + $this->assertSame(2, $token->getLine()); + } + } + } + + public function testGetEndLineWithUnscopedNamespace() + { + $tokenStream = new PHP_Token_Stream(TEST_FILES_PATH . 'classInNamespace.php'); + foreach($tokenStream as $token) { + if($token instanceOf PHP_Token_NAMESPACE) { + $this->assertSame(2, $token->getEndLine()); + } + } + } + public function testGetStartLineWithScopedNamespace() + { + $tokenStream = new PHP_Token_Stream(TEST_FILES_PATH . 'classInScopedNamespace.php'); + foreach($tokenStream as $token) { + if($token instanceOf PHP_Token_NAMESPACE) { + $this->assertSame(2, $token->getLine()); + } + } + } + + public function testGetEndLineWithScopedNamespace() + { + $tokenStream = new PHP_Token_Stream(TEST_FILES_PATH . 'classInScopedNamespace.php'); + foreach($tokenStream as $token) { + if($token instanceOf PHP_Token_NAMESPACE) { + $this->assertSame(8, $token->getEndLine()); + } + } + } + +} diff --git a/vendor/phpunit/php-token-stream/Tests/TokenTest.php b/vendor/phpunit/php-token-stream/Tests/TokenTest.php new file mode 100644 index 0000000..60cd0c7 --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/TokenTest.php @@ -0,0 +1,85 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @since File available since Release 1.0.0 + */ + +if (!defined('TEST_FILES_PATH')) { + define( + 'TEST_FILES_PATH', + dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR + ); +} + +require_once 'PHP/Token/Stream.php'; + +/** + * Tests for the PHP_Token class. + * + * @package PHP_TokenStream + * @subpackage Tests + * @author Sebastian Bergmann + * @copyright 2009-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/php-token-stream/ + * @since Class available since Release 1.0.0 + */ +class PHP_TokenTest extends PHPUnit_Framework_TestCase +{ + /** + * @covers PHP_Token::__construct + * @covers PHP_Token::__toString + */ + public function testToString() + { + $this->markTestIncomplete(); + } + + /** + * @covers PHP_Token::__construct + * @covers PHP_Token::getLine + */ + public function testGetLine() + { + $this->markTestIncomplete(); + } +} diff --git a/vendor/phpunit/php-token-stream/Tests/_files/classExtendsNamespacedClass.php b/vendor/phpunit/php-token-stream/Tests/_files/classExtendsNamespacedClass.php new file mode 100644 index 0000000..560eec9 --- /dev/null +++ b/vendor/phpunit/php-token-stream/Tests/_files/classExtendsNamespacedClass.php @@ -0,0 +1,10 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-token-stream/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php b/vendor/phpunit/php-token-stream/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php new file mode 100644 index 0000000..bf9d520 --- /dev/null +++ b/vendor/phpunit/php-token-stream/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php @@ -0,0 +1,22 @@ +getTokens(); + + if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE || + $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { + + $phpcsFile->addError( + 'Concatenation operator must be surrounded by whitespace', + $stackPtr + ); + } + } +} diff --git a/vendor/phpunit/php-token-stream/build/PHPCS/ruleset.xml b/vendor/phpunit/php-token-stream/build/PHPCS/ruleset.xml new file mode 100644 index 0000000..402f214 --- /dev/null +++ b/vendor/phpunit/php-token-stream/build/PHPCS/ruleset.xml @@ -0,0 +1,35 @@ + + + Sebastian Bergmann's coding standard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-token-stream/build/phpmd.xml b/vendor/phpunit/php-token-stream/build/phpmd.xml new file mode 100644 index 0000000..23ecb8b --- /dev/null +++ b/vendor/phpunit/php-token-stream/build/phpmd.xml @@ -0,0 +1,27 @@ + + + + Sebastian Bergmann's ruleset + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/php-token-stream/composer.json b/vendor/phpunit/php-token-stream/composer.json new file mode 100644 index 0000000..d67c034 --- /dev/null +++ b/vendor/phpunit/php-token-stream/composer.json @@ -0,0 +1,33 @@ +{ + "name": "phpunit/php-token-stream", + "description": "Wrapper around PHP's tokenizer extension.", + "type": "library", + "keywords": [ + "tokenizer" + ], + "homepage": "http://www.phpunit.de/", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "require": { + "php": ">=5.3.3", + "ext-tokenizer": "*" + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "include-path": [ + "" + ] +} diff --git a/vendor/phpunit/php-token-stream/package-composer.json b/vendor/phpunit/php-token-stream/package-composer.json new file mode 100644 index 0000000..a42eb94 --- /dev/null +++ b/vendor/phpunit/php-token-stream/package-composer.json @@ -0,0 +1,19 @@ +{ + "name": "phpunit/php-token-stream", + "keywords": [ "tokenizer" ], + "license": "BSD-3-Clause", + "homepage": "http://www.phpunit.de/", + "dependency_map": { }, + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "autoload": { + "classmap": [ "PHP/" ] + }, + "include_path": [ + "" + ], + "version": false, + "time": false +} \ No newline at end of file diff --git a/vendor/phpunit/php-token-stream/package.xml b/vendor/phpunit/php-token-stream/package.xml new file mode 100644 index 0000000..d2f758d --- /dev/null +++ b/vendor/phpunit/php-token-stream/package.xml @@ -0,0 +1,70 @@ + + + PHP_TokenStream + pear.phpunit.de + Wrapper around PHP's tokenizer extension. + Wrapper around PHP's tokenizer extension. + + Sebastian Bergmann + sb + sb@sebastian-bergmann.de + yes + + 2012-10-05 + + 1.1.5 + 1.1.0 + + + stable + stable + + The BSD 3-Clause License + http://github.com/sebastianbergmann/php-token-stream/tree + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.9.4 + + + tokenizer + + + + + diff --git a/vendor/phpunit/php-token-stream/phpunit.xml.dist b/vendor/phpunit/php-token-stream/phpunit.xml.dist new file mode 100644 index 0000000..6d307c4 --- /dev/null +++ b/vendor/phpunit/php-token-stream/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + + + Tests + + + + + + + + + + + + PHP + + + diff --git a/vendor/phpunit/phpunit-mock-objects/CONTRIBUTING.md b/vendor/phpunit/phpunit-mock-objects/CONTRIBUTING.md new file mode 100644 index 0000000..b290539 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/CONTRIBUTING.md @@ -0,0 +1,5 @@ +Pull Requests for bug fixes should be made against the current release branch (1.2). + +Pull Requests for new features should be made against master. + +For further notes please refer to [https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md) diff --git a/vendor/phpunit/phpunit-mock-objects/ChangeLog.markdown b/vendor/phpunit/phpunit-mock-objects/ChangeLog.markdown new file mode 100644 index 0000000..b55b125 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/ChangeLog.markdown @@ -0,0 +1,17 @@ +PHPUnit_MockObject 1.2 +====================== + +This is the list of changes for the PHPUnit_MockObject 1.2 release series. + +PHPUnit_MockObject 1.2.1 +------------------------ + +* No changes. + +PHPUnit_MockObject 1.2.0 +------------------------ + +* Implemented #47: Make cloning of arguments passed to mocked methods optional. +* Implemented #84: `getMockFromWsdl()` now works with namespaces. +* Fixed #90: Mocks with a fixed class name could only be created once. + diff --git a/vendor/phpunit/phpunit-mock-objects/LICENSE b/vendor/phpunit/phpunit-mock-objects/LICENSE new file mode 100644 index 0000000..e91e033 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/LICENSE @@ -0,0 +1,33 @@ +PHPUnit_MockObject + +Copyright (c) 2002-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php new file mode 100644 index 0000000..296a1ac --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php @@ -0,0 +1,100 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + 'phpunit_framework_mockobject_builder_identity' => '/Framework/MockObject/Builder/Identity.php', + 'phpunit_framework_mockobject_builder_invocationmocker' => '/Framework/MockObject/Builder/InvocationMocker.php', + 'phpunit_framework_mockobject_builder_match' => '/Framework/MockObject/Builder/Match.php', + 'phpunit_framework_mockobject_builder_methodnamematch' => '/Framework/MockObject/Builder/MethodNameMatch.php', + 'phpunit_framework_mockobject_builder_namespace' => '/Framework/MockObject/Builder/Namespace.php', + 'phpunit_framework_mockobject_builder_parametersmatch' => '/Framework/MockObject/Builder/ParametersMatch.php', + 'phpunit_framework_mockobject_builder_stub' => '/Framework/MockObject/Builder/Stub.php', + 'phpunit_framework_mockobject_generator' => '/Framework/MockObject/Generator.php', + 'phpunit_framework_mockobject_invocation' => '/Framework/MockObject/Invocation.php', + 'phpunit_framework_mockobject_invocation_object' => '/Framework/MockObject/Invocation/Object.php', + 'phpunit_framework_mockobject_invocation_static' => '/Framework/MockObject/Invocation/Static.php', + 'phpunit_framework_mockobject_invocationmocker' => '/Framework/MockObject/InvocationMocker.php', + 'phpunit_framework_mockobject_invokable' => '/Framework/MockObject/Invokable.php', + 'phpunit_framework_mockobject_matcher' => '/Framework/MockObject/Matcher.php', + 'phpunit_framework_mockobject_matcher_anyinvokedcount' => '/Framework/MockObject/Matcher/AnyInvokedCount.php', + 'phpunit_framework_mockobject_matcher_anyparameters' => '/Framework/MockObject/Matcher/AnyParameters.php', + 'phpunit_framework_mockobject_matcher_invocation' => '/Framework/MockObject/Matcher/Invocation.php', + 'phpunit_framework_mockobject_matcher_invokedatindex' => '/Framework/MockObject/Matcher/InvokedAtIndex.php', + 'phpunit_framework_mockobject_matcher_invokedatleastonce' => '/Framework/MockObject/Matcher/InvokedAtLeastOnce.php', + 'phpunit_framework_mockobject_matcher_invokedcount' => '/Framework/MockObject/Matcher/InvokedCount.php', + 'phpunit_framework_mockobject_matcher_invokedrecorder' => '/Framework/MockObject/Matcher/InvokedRecorder.php', + 'phpunit_framework_mockobject_matcher_methodname' => '/Framework/MockObject/Matcher/MethodName.php', + 'phpunit_framework_mockobject_matcher_parameters' => '/Framework/MockObject/Matcher/Parameters.php', + 'phpunit_framework_mockobject_matcher_statelessinvocation' => '/Framework/MockObject/Matcher/StatelessInvocation.php', + 'phpunit_framework_mockobject_mockbuilder' => '/Framework/MockObject/MockBuilder.php', + 'phpunit_framework_mockobject_mockobject' => '/Framework/MockObject/MockObject.php', + 'phpunit_framework_mockobject_stub' => '/Framework/MockObject/Stub.php', + 'phpunit_framework_mockobject_stub_consecutivecalls' => '/Framework/MockObject/Stub/ConsecutiveCalls.php', + 'phpunit_framework_mockobject_stub_exception' => '/Framework/MockObject/Stub/Exception.php', + 'phpunit_framework_mockobject_stub_matchercollection' => '/Framework/MockObject/Stub/MatcherCollection.php', + 'phpunit_framework_mockobject_stub_return' => '/Framework/MockObject/Stub/Return.php', + 'phpunit_framework_mockobject_stub_returnargument' => '/Framework/MockObject/Stub/ReturnArgument.php', + 'phpunit_framework_mockobject_stub_returncallback' => '/Framework/MockObject/Stub/ReturnCallback.php', + 'phpunit_framework_mockobject_stub_returnself' => '/Framework/MockObject/Stub/ReturnSelf.php', + 'phpunit_framework_mockobject_stub_returnvaluemap' => '/Framework/MockObject/Stub/ReturnValueMap.php', + 'phpunit_framework_mockobject_verifiable' => '/Framework/MockObject/Verifiable.php' + ); + + $path = dirname(dirname(dirname(__FILE__))); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php.in b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php.in new file mode 100644 index 0000000..fe019c0 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Autoload.php.in @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.1.0 + */ + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(dirname(__FILE__))); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Identity.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Identity.php new file mode 100644 index 0000000..83177f4 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Identity.php @@ -0,0 +1,70 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Builder interface for unique identifiers. + * + * Defines the interface for recording unique identifiers. The identifiers + * can be used to define the invocation order of expectations. The expectation + * is recorded using id() and then defined in order using + * PHPUnit_Framework_MockObject_Builder_Match::after(). + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Builder_Identity +{ + /** + * Sets the identification of the expectation to $id. + * + * @note The identifier is unique per mock object. + * @param string $id Unique identifiation of expectation. + */ + public function id($id); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php new file mode 100644 index 0000000..7fc4fed --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php @@ -0,0 +1,193 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Builder for mocked or stubbed invocations. + * + * Provides methods for building expectations without having to resort to + * instantiating the various matchers manually. These methods also form a + * more natural way of reading the expectation. This class should be together + * with the test case PHPUnit_Framework_MockObject_TestCase. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Builder_InvocationMocker implements PHPUnit_Framework_MockObject_Builder_MethodNameMatch +{ + /** + * @var PHPUnit_Framework_MockObject_Stub_MatcherCollection + */ + protected $collection; + + /** + * @var PHPUnit_Framework_MockObject_Matcher + */ + protected $matcher; + + /** + * @param PHPUnit_Framework_MockObject_Stub_MatcherCollection $collection + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher + */ + public function __construct(PHPUnit_Framework_MockObject_Stub_MatcherCollection $collection, PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher) + { + $this->collection = $collection; + $this->matcher = new PHPUnit_Framework_MockObject_Matcher( + $invocationMatcher + ); + + $this->collection->addMatcher($this->matcher); + } + + /** + * @return PHPUnit_Framework_MockObject_Matcher + */ + public function getMatcher() + { + return $this->matcher; + } + + /** + * @param mixed $id + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function id($id) + { + $this->collection->registerId($id, $this); + + return $this; + } + + /** + * @param PHPUnit_Framework_MockObject_Stub $stub + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function will(PHPUnit_Framework_MockObject_Stub $stub) + { + $this->matcher->stub = $stub; + + return $this; + } + + /** + * @param mixed $id + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function after($id) + { + $this->matcher->afterMatchBuilderId = $id; + + return $this; + } + + /** + * @param mixed $argument, ... + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function with() + { + $args = func_get_args(); + + if ($this->matcher->methodNameMatcher === NULL) { + throw new PHPUnit_Framework_Exception( + 'Method name matcher is not defined, cannot define parameter ' . + ' matcher without one' + ); + } + + if ($this->matcher->parametersMatcher !== NULL) { + throw new PHPUnit_Framework_Exception( + 'Parameter matcher is already defined, cannot redefine' + ); + } + + $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_Parameters($args); + + return $this; + } + + /** + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function withAnyParameters() + { + if ($this->matcher->methodNameMatcher === NULL) { + throw new PHPUnit_Framework_Exception( + 'Method name matcher is not defined, cannot define parameter ' . + 'matcher without one' + ); + } + + if ($this->matcher->parametersMatcher !== NULL) { + throw new PHPUnit_Framework_Exception( + 'Parameter matcher is already defined, cannot redefine' + ); + } + + $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters; + + return $this; + } + + /** + * @param PHPUnit_Framework_Constraint|string $constraint + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function method($constraint) + { + if ($this->matcher->methodNameMatcher !== NULL) { + throw new PHPUnit_Framework_Exception( + 'Method name matcher is already defined, cannot redefine' + ); + } + + $this->matcher->methodNameMatcher = new PHPUnit_Framework_MockObject_Matcher_MethodName($constraint); + + return $this; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Match.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Match.php new file mode 100644 index 0000000..ce998cb --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Match.php @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Builder interface for invocation order matches. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Builder_Match extends PHPUnit_Framework_MockObject_Builder_Stub +{ + /** + * Defines the expectation which must occur before the current is valid. + * + * @param string $id The identification of the expectation that should + * occur before this one. + * @return PHPUnit_Framework_MockObject_Builder_Stub + */ + public function after($id); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php new file mode 100644 index 0000000..6346842 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php @@ -0,0 +1,68 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Builder interface for matcher of method names. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Builder_MethodNameMatch extends PHPUnit_Framework_MockObject_Builder_ParametersMatch +{ + /** + * Adds a new method name match and returns the parameter match object for + * further matching possibilities. + * + * @param PHPUnit_Framework_Constraint $name + * Constraint for matching method, if a string is passed it will use + * the PHPUnit_Framework_Constraint_IsEqual. + * @return PHPUnit_Framework_MockObject_Builder_ParametersMatch + */ + public function method($name); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Namespace.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Namespace.php new file mode 100644 index 0000000..e14a9a7 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Namespace.php @@ -0,0 +1,79 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Interface for builders which can register builders with a given identification. + * + * This interface relates to PHPUnit_Framework_MockObject_Builder_Identity. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Builder_Namespace +{ + /** + * Looks up the match builder with identification $id and returns it. + * + * @param string $id The identifiction of the match builder. + * @return PHPUnit_Framework_MockObject_Builder_Match + */ + public function lookupId($id); + + /** + * Registers the match builder $builder with the identification $id. The + * builder can later be looked up using lookupId() to figure out if it + * has been invoked. + * + * @param string $id + * The identification of the match builder. + * @param PHPUnit_Framework_MockObject_Builder_Match $builder + * The builder which is being registered. + */ + public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php new file mode 100644 index 0000000..c907456 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php @@ -0,0 +1,89 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Builder interface for parameter matchers. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Builder_ParametersMatch extends PHPUnit_Framework_MockObject_Builder_Match +{ + /** + * Sets the parameters to match for, each parameter to this funtion will + * be part of match. To perform specific matches or constraints create a + * new PHPUnit_Framework_Constraint and use it for the parameter. + * If the parameter value is not a constraint it will use the + * PHPUnit_Framework_Constraint_IsEqual for the value. + * + * Some examples: + * + * // match first parameter with value 2 + * $b->with(2); + * // match first parameter with value 'smock' and second identical to 42 + * $b->with('smock', new PHPUnit_Framework_Constraint_IsEqual(42)); + * + * + * @return PHPUnit_Framework_MockObject_Builder_ParametersMatch + */ + public function with(); + + /** + * Sets a matcher which allows any kind of parameters. + * + * Some examples: + * + * // match any number of parameters + * $b->withAnyParamers(); + * + * + * @return PHPUnit_Framework_MockObject_Matcher_AnyParameters + */ + public function withAnyParameters(); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Stub.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Stub.php new file mode 100644 index 0000000..25c31c9 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Stub.php @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Builder interface for stubs which are actions replacing an invocation. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Builder_Stub extends PHPUnit_Framework_MockObject_Builder_Identity +{ + /** + * Stubs the matching method with the stub object $stub. Any invocations of + * the matched method will now be handled by the stub instead. + * + * @param PHPUnit_Framework_MockObject_Stub $stub The stub object. + * @return PHPUnit_Framework_MockObject_Builder_Identity + */ + public function will(PHPUnit_Framework_MockObject_Stub $stub); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator.php new file mode 100644 index 0000000..45f0d22 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator.php @@ -0,0 +1,811 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Mock Object Code Generator + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Generator +{ + /** + * @var array + */ + protected static $cache = array(); + + /** + * @var array + */ + protected static $blacklistedMethodNames = array( + '__clone' => TRUE, + 'abstract' => TRUE, + 'and' => TRUE, + 'array' => TRUE, + 'as' => TRUE, + 'break' => TRUE, + 'case' => TRUE, + 'catch' => TRUE, + 'class' => TRUE, + 'clone' => TRUE, + 'const' => TRUE, + 'continue' => TRUE, + 'declare' => TRUE, + 'default' => TRUE, + 'die' => TRUE, + 'do' => TRUE, + 'echo' => TRUE, + 'else' => TRUE, + 'elseif' => TRUE, + 'empty' => TRUE, + 'enddeclare' => TRUE, + 'endfor' => TRUE, + 'endforeach' => TRUE, + 'endif' => TRUE, + 'endswitch' => TRUE, + 'endwhile' => TRUE, + 'eval' => TRUE, + 'exit' => TRUE, + 'expects' => TRUE, + 'extends' => TRUE, + 'final' => TRUE, + 'for' => TRUE, + 'foreach' => TRUE, + 'function' => TRUE, + 'global' => TRUE, + 'goto' => TRUE, + 'if' => TRUE, + 'implements' => TRUE, + 'include' => TRUE, + 'include_once' => TRUE, + 'instanceof' => TRUE, + 'interface' => TRUE, + 'isset' => TRUE, + 'list' => TRUE, + 'namespace' => TRUE, + 'new' => TRUE, + 'or' => TRUE, + 'print' => TRUE, + 'private' => TRUE, + 'protected' => TRUE, + 'public' => TRUE, + 'require' => TRUE, + 'require_once' => TRUE, + 'return' => TRUE, + 'static' => TRUE, + 'staticExpects' => TRUE, + 'switch' => TRUE, + 'throw' => TRUE, + 'try' => TRUE, + 'unset' => TRUE, + 'use' => TRUE, + 'var' => TRUE, + 'while' => TRUE, + 'xor' => TRUE + ); + + /** + * @var boolean + */ + protected static $soapLoaded = NULL; + + /** + * Returns a mock object for the specified class. + * + * @param string $originalClassName + * @param array $methods + * @param array $arguments + * @param string $mockClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param boolean $cloneArguments + * @return object + * @throws InvalidArgumentException + * @since Method available since Release 1.0.0 + */ + public static function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = TRUE) + { + if (!is_string($originalClassName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($mockClassName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'string'); + } + + if (!is_array($methods) && !is_null($methods)) { + throw new InvalidArgumentException; + } + + if (NULL !== $methods) { + foreach ($methods as $method) { + if (!preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Cannot stub or mock method with invalid name "%s"', + $method + ) + ); + } + } + if ($methods != array_unique($methods)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Cannot stub or mock using a method list that contains duplicates: "%s"', + implode(', ', $methods) + ) + ); + } + } + + if ($mockClassName != '' && class_exists($mockClassName, FALSE)) { + $reflect = new ReflectionClass($mockClassName); + if (!$reflect->implementsInterface("PHPUnit_Framework_MockObject_MockObject")) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Class "%s" already exists.', + $mockClassName + ) + ); + } + } + + $mock = self::generate( + $originalClassName, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + + return self::getObject( + $mock['code'], + $mock['mockClassName'], + $originalClassName, + $callOriginalConstructor, + $callAutoload, + $arguments + ); + } + + /** + * @param string $code + * @param string $className + * @param string $originalClassName + * @param string $callOriginalConstructor + * @param string $callAutoload + * @param array $arguments + * @return object + */ + protected static function getObject($code, $className, $originalClassName = '', $callOriginalConstructor = FALSE, $callAutoload = FALSE, array $arguments = array()) + { + if (!class_exists($className, FALSE)) { + eval($code); + } + + if ($callOriginalConstructor && + !interface_exists($originalClassName, $callAutoload)) { + if (count($arguments) == 0) { + $object = new $className; + } else { + $class = new ReflectionClass($className); + $object = $class->newInstanceArgs($arguments); + } + } else { + // Use a trick to create a new object of a class + // without invoking its constructor. + $object = unserialize( + sprintf('O:%d:"%s":0:{}', strlen($className), $className) + ); + } + + if ($object instanceof PHPUnit_Framework_MockObject_MockObject) { + $object->__phpunit_setId(); + } + + return $object; + } + + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods to mock can be specified with + * the last parameter + * + * @param string $originalClassName + * @param array $arguments + * @param string $mockClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param array $mockedMethods + * @param boolean $cloneArguments + * @return object + * @since Method available since Release 1.0.0 + * @throws InvalidArgumentException + */ + public static function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $mockedMethods = array(), $cloneArguments = TRUE) + { + if (!is_string($originalClassName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($mockClassName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string'); + } + + if (class_exists($originalClassName, $callAutoload) || + interface_exists($originalClassName, $callAutoload)) { + $methods = array(); + $reflector = new ReflectionClass($originalClassName); + + foreach ($reflector->getMethods() as $method) { + if ($method->isAbstract() || in_array($method->getName(), $mockedMethods)) { + $methods[] = $method->getName(); + } + } + + if (empty($methods)) { + $methods = NULL; + } + + return self::getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + } else { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Class "%s" does not exist.', + $originalClassName + ) + ); + } + } + + /** + * Returns an object for the specified trait. + * + * @param string $traitName + * @param array $arguments + * @param string $traitClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @return object + * @since Method available since Release 1.1.0 + * @throws InvalidArgumentException + */ + public static function getObjectForTrait($traitName, array $arguments = array(), $traitClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) + { + if (!is_string($traitName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($traitClassName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string'); + } + + if (!trait_exists($traitName, $callAutoload)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Trait "%s" does not exist.', + $traitName + ) + ); + } + + $className = self::generateClassName( + $traitName, $traitClassName, 'Trait_' + ); + + $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' . + DIRECTORY_SEPARATOR; + $classTemplate = new Text_Template( + $templateDir . 'trait_class.tpl' + ); + + $classTemplate->setVar( + array( + 'class_name' => $className['className'], + 'trait_name' => $traitName + ) + ); + + return self::getObject( + $classTemplate->render(), + $className['className'] + ); + } + + /** + * @param string $originalClassName + * @param array $methods + * @param string $mockClassName + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param boolean $cloneArguments + * @return array + */ + public static function generate($originalClassName, array $methods = NULL, $mockClassName = '', $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = TRUE) + { + if ($mockClassName == '') { + $key = md5( + $originalClassName . + serialize($methods) . + serialize($callOriginalClone) + ); + + if (isset(self::$cache[$key])) { + return self::$cache[$key]; + } + } + + $mock = self::generateMock( + $originalClassName, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + + if (isset($key)) { + self::$cache[$key] = $mock; + } + + return $mock; + } + + /** + * @param string $wsdlFile + * @param string $originalClassName + * @param array $methods + * @param array $options + * @return array + */ + public static function generateClassFromWsdl($wsdlFile, $originalClassName, array $methods = array(), array $options = array()) + { + if (self::$soapLoaded === NULL) { + self::$soapLoaded = extension_loaded('soap'); + } + + if (self::$soapLoaded) { + $client = new SOAPClient($wsdlFile, $options); + $_methods = array_unique($client->__getFunctions()); + unset($client); + + $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . + 'Generator' . DIRECTORY_SEPARATOR; + $methodTemplate = new Text_Template( + $templateDir . 'wsdl_method.tpl' + ); + $methodsBuffer = ''; + + foreach ($_methods as $method) { + $nameStart = strpos($method, ' ') + 1; + $nameEnd = strpos($method, '('); + $name = substr($method, $nameStart, $nameEnd - $nameStart); + + if (empty($methods) || in_array($name, $methods)) { + $args = explode( + ',', + substr( + $method, + $nameEnd + 1, + strpos($method, ')') - $nameEnd - 1 + ) + ); + $numArgs = count($args); + + for ($i = 0; $i < $numArgs; $i++) { + $args[$i] = substr($args[$i], strpos($args[$i], '$')); + } + + $methodTemplate->setVar( + array( + 'method_name' => $name, + 'arguments' => join(', ', $args) + ) + ); + + $methodsBuffer .= $methodTemplate->render(); + } + } + + $optionsBuffer = 'array('; + foreach ($options as $key => $value) { + $optionsBuffer .= $key . ' => ' . $value; + } + + $optionsBuffer .= ')'; + + $classTemplate = new Text_Template( + $templateDir . 'wsdl_class.tpl' + ); + + $namespace = ''; + if(strpos($originalClassName, '\\') !== FALSE) { + $parts = explode('\\', $originalClassName); + $originalClassName = array_pop($parts); + $namespace = 'namespace ' . join('\\', $parts) . ';'; + } + + $classTemplate->setVar( + array( + 'namespace' => $namespace, + 'class_name' => $originalClassName, + 'wsdl' => $wsdlFile, + 'options' => $optionsBuffer, + 'methods' => $methodsBuffer + ) + ); + + return $classTemplate->render(); + } else { + throw new PHPUnit_Framework_Exception( + 'The SOAP extension is required to generate a mock object ' . + 'from WSDL.' + ); + } + } + + /** + * @param string $originalClassName + * @param array|null $methods + * @param string $mockClassName + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param boolean $cloneArguments + * @return array + */ + protected static function generateMock($originalClassName, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments = TRUE) + { + $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' . + DIRECTORY_SEPARATOR; + $classTemplate = new Text_Template( + $templateDir . 'mocked_class.tpl' + ); + $cloneTemplate = ''; + $isClass = FALSE; + $isInterface = FALSE; + + $mockClassName = self::generateClassName( + $originalClassName, $mockClassName, 'Mock_' + ); + + if (class_exists($mockClassName['fullClassName'], $callAutoload)) { + $isClass = TRUE; + } else { + if (interface_exists($mockClassName['fullClassName'], $callAutoload)) { + $isInterface = TRUE; + } + } + + if (!class_exists($mockClassName['fullClassName'], $callAutoload) && + !interface_exists($mockClassName['fullClassName'], $callAutoload)) { + $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n"; + + if (!empty($mockClassName['namespaceName'])) { + $prologue = 'namespace ' . $mockClassName['namespaceName'] . + " {\n\n" . $prologue . "}\n\n" . + "namespace {\n\n"; + + $epilogue = "\n\n}"; + } + + $cloneTemplate = new Text_Template( + $templateDir . 'mocked_clone.tpl' + ); + } else { + $class = new ReflectionClass($mockClassName['fullClassName']); + + if ($class->isFinal()) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Class "%s" is declared "final" and cannot be mocked.', + $mockClassName['fullClassName'] + ) + ); + } + + if ($class->hasMethod('__clone')) { + $cloneMethod = $class->getMethod('__clone'); + + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $cloneTemplate = new Text_Template( + $templateDir . 'unmocked_clone.tpl' + ); + } else { + $cloneTemplate = new Text_Template( + $templateDir . 'mocked_clone.tpl' + ); + } + } + } else { + $cloneTemplate = new Text_Template( + $templateDir . 'mocked_clone.tpl' + ); + } + } + + if (is_object($cloneTemplate)) { + $cloneTemplate = $cloneTemplate->render(); + } + + if (is_array($methods) && empty($methods) && + ($isClass || $isInterface)) { + $methods = get_class_methods($mockClassName['fullClassName']); + } + + if (!is_array($methods)) { + $methods = array(); + } + + $mockedMethods = ''; + + if (isset($class)) { + foreach ($methods as $methodName) { + try { + $method = $class->getMethod($methodName); + + if (self::canMockMethod($method)) { + $mockedMethods .= self::generateMockedMethodDefinitionFromExisting( + $templateDir, $method, $cloneArguments + ); + } + } + + catch (ReflectionException $e) { + $mockedMethods .= self::generateMockedMethodDefinition( + $templateDir, $mockClassName['fullClassName'], $methodName, $cloneArguments + ); + } + } + } else { + foreach ($methods as $methodName) { + $mockedMethods .= self::generateMockedMethodDefinition( + $templateDir, $mockClassName['fullClassName'], $methodName, $cloneArguments + ); + } + } + + $classTemplate->setVar( + array( + 'prologue' => isset($prologue) ? $prologue : '', + 'epilogue' => isset($epilogue) ? $epilogue : '', + 'class_declaration' => self::generateMockClassDeclaration( + $mockClassName, $isInterface + ), + 'clone' => $cloneTemplate, + 'mock_class_name' => $mockClassName['className'], + 'mocked_methods' => $mockedMethods + ) + ); + + return array( + 'code' => $classTemplate->render(), + 'mockClassName' => $mockClassName['className'] + ); + } + + /** + * @param string $originalClassName + * @param string $className + * @param string $prefix + * @return array + */ + protected static function generateClassName($originalClassName, $className, $prefix) + { + if ($originalClassName[0] == '\\') { + $originalClassName = substr($originalClassName, 1); + } + + $classNameParts = explode('\\', $originalClassName); + + if (count($classNameParts) > 1) { + $originalClassName = array_pop($classNameParts); + $namespaceName = join('\\', $classNameParts); + $fullClassName = $namespaceName . '\\' . $originalClassName; + } else { + $namespaceName = ''; + $fullClassName = $originalClassName; + } + + if ($className == '') { + do { + $className = $prefix . $originalClassName . '_' . + substr(md5(microtime()), 0, 8); + } + while (class_exists($className, FALSE)); + } + + return array( + 'className' => $className, + 'originalClassName' => $originalClassName, + 'fullClassName' => $fullClassName, + 'namespaceName' => $namespaceName + ); + } + + /** + * @param array $mockClassName + * @param boolean $isInterface + * @return array + */ + protected static function generateMockClassDeclaration(array $mockClassName, $isInterface) + { + $buffer = 'class '; + + if ($isInterface) { + $buffer .= sprintf( + "%s implements PHPUnit_Framework_MockObject_MockObject, %s%s", + $mockClassName['className'], + !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', + $mockClassName['originalClassName'] + ); + } else { + $buffer .= sprintf( + "%s extends %s%s implements PHPUnit_Framework_MockObject_MockObject", + $mockClassName['className'], + !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', + $mockClassName['originalClassName'] + ); + } + + return $buffer; + } + + /** + * @param string $templateDir + * @param ReflectionMethod $method + * @param boolean $cloneArguments + * @return string + */ + protected static function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method, $cloneArguments = TRUE) + { + if ($method->isPrivate()) { + $modifier = 'private'; + } + + else if ($method->isProtected()) { + $modifier = 'protected'; + } + + else { + $modifier = 'public'; + } + + if ($method->isStatic()) { + $static = TRUE; + } else { + $static = FALSE; + } + + if ($method->returnsReference()) { + $reference = '&'; + } else { + $reference = ''; + } + + return self::generateMockedMethodDefinition( + $templateDir, + $method->getDeclaringClass()->getName(), + $method->getName(), + $cloneArguments, + $modifier, + PHPUnit_Util_Class::getMethodParameters($method), + PHPUnit_Util_Class::getMethodParameters($method, TRUE), + $reference, + $static + ); + } + + /** + * @param string $templateDir + * @param string $className + * @param string $methodName + * @param boolean $cloneArguments + * @param string $modifier + * @param string $arguments_decl + * @param string $arguments_call + * @param string $reference + * @param boolean $static + * @return string + */ + protected static function generateMockedMethodDefinition($templateDir, $className, $methodName, $cloneArguments = TRUE, $modifier = 'public', $arguments_decl = '', $arguments_call = '', $reference = '', $static = FALSE) + { + if ($static) { + $template = new Text_Template( + $templateDir . 'mocked_static_method.tpl' + ); + } else { + $template = new Text_Template( + $templateDir . 'mocked_object_method.tpl' + ); + } + + $template->setVar( + array( + 'arguments_decl' => $arguments_decl, + 'arguments_call' => $arguments_call, + 'arguments_count' => !empty($arguments_call) ? count(explode(',', $arguments_call)) : 0, + 'class_name' => $className, + 'method_name' => $methodName, + 'modifier' => $modifier, + 'reference' => $reference, + 'clone_arguments' => $cloneArguments ? 'TRUE' : 'FALSE' + ) + ); + + return $template->render(); + } + + /** + * @param ReflectionMethod $method + * @return boolean + */ + protected static function canMockMethod(ReflectionMethod $method) + { + if ($method->isConstructor() || $method->isFinal() || + isset(self::$blacklistedMethodNames[$method->getName()])) { + return FALSE; + } + + return TRUE; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist new file mode 100644 index 0000000..5e22d7b --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist @@ -0,0 +1,60 @@ +{prologue}{class_declaration} +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + +{clone}{mocked_methods} + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +}{epilogue} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist new file mode 100644 index 0000000..e8d7ad0 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist @@ -0,0 +1,5 @@ + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist new file mode 100644 index 0000000..e2f55d9 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist @@ -0,0 +1,22 @@ + + {modifier} function {reference}{method_name}({arguments_decl}) + { + $arguments = array({arguments_call}); + $count = func_num_args(); + + if ($count > {arguments_count}) { + $_arguments = func_get_args(); + + for ($i = {arguments_count}; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + '{class_name}', '{method_name}', $arguments, $this, {clone_arguments} + ) + ); + + return $result; + } diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist new file mode 100644 index 0000000..06df070 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist @@ -0,0 +1,22 @@ + + {modifier} static function {reference}{method_name}({arguments_decl}) + { + $arguments = array({arguments_call}); + $count = func_num_args(); + + if ($count > {arguments_count}) { + $_arguments = func_get_args(); + + for ($i = {arguments_count}; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = self::__phpunit_getStaticInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Static( + '{class_name}', '{method_name}', $arguments, {clone_arguments} + ) + ); + + return $result; + } diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist new file mode 100644 index 0000000..48b4bbf --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist @@ -0,0 +1,4 @@ +class {class_name} +{ + use {trait_name}; +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist new file mode 100644 index 0000000..df829bd --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist @@ -0,0 +1,6 @@ + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + parent::__clone(); + } diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist new file mode 100644 index 0000000..05ad1d7 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist @@ -0,0 +1,9 @@ +{namespace} + +class {class_name} extends \SOAPClient +{ + public function __construct($wsdl, array $options) + { + parent::__construct('{wsdl}', $options); + } +{methods}} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist new file mode 100644 index 0000000..bb16e76 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist @@ -0,0 +1,4 @@ + + public function {method_name}({arguments}) + { + } diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation.php new file mode 100644 index 0000000..70666ac --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation.php @@ -0,0 +1,58 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Interface for invocations. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Invocation +{ +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Object.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Object.php new file mode 100644 index 0000000..70754c5 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Object.php @@ -0,0 +1,75 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Represents a non-static invocation. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Invocation_Object extends PHPUnit_Framework_MockObject_Invocation_Static +{ + /** + * @var object + */ + public $object; + + /** + * @param string $className + * @param string $methodname + * @param array $parameters + * @param object $object + * @param object $cloneObjects + */ + public function __construct($className, $methodName, array $parameters, $object, $cloneObjects = FALSE) + { + parent::__construct($className, $methodName, $parameters, $cloneObjects); + $this->object = $object; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Static.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Static.php new file mode 100644 index 0000000..4155788 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Static.php @@ -0,0 +1,191 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Represents a static invocation. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Invocation_Static implements PHPUnit_Framework_MockObject_Invocation, PHPUnit_Framework_SelfDescribing +{ + /** + * @var array + */ + protected static $uncloneableExtensions = array( + 'mysqli' => TRUE, + 'SQLite' => TRUE, + 'sqlite3' => TRUE, + 'tidy' => TRUE, + 'xmlwriter' => TRUE, + 'xsl' => TRUE + ); + + /** + * @var array + */ + protected static $uncloneableClasses = array( + 'Closure', + 'COMPersistHelper', + 'IteratorIterator', + 'RecursiveIteratorIterator', + 'SplFileObject', + 'PDORow', + 'ZipArchive' + ); + + /** + * @var string + */ + public $className; + + /** + * @var string + */ + public $methodName; + + /** + * @var array + */ + public $parameters; + + /** + * @param string $className + * @param string $methodname + * @param array $parameters + * @param boolean $cloneObjects + */ + public function __construct($className, $methodName, array $parameters, $cloneObjects = FALSE) + { + $this->className = $className; + $this->methodName = $methodName; + $this->parameters = $parameters; + + if (!$cloneObjects) { + return; + } + + foreach ($this->parameters as $key => $value) { + if (is_object($value)) { + $this->parameters[$key] = $this->cloneObject($value); + } + } + } + + /** + * @return string + */ + public function toString() + { + return sprintf( + "%s::%s(%s)", + + $this->className, + $this->methodName, + join( + ', ', + array_map( + array('PHPUnit_Util_Type', 'shortenedExport'), + $this->parameters + ) + ) + ); + } + + /** + * @param object $original + * @return object + */ + protected function cloneObject($original) + { + $cloneable = NULL; + $object = new ReflectionObject($original); + + // Check the blacklist before asking PHP reflection to work around + // https://bugs.php.net/bug.php?id=53967 + if ($object->isInternal() && + isset(self::$uncloneableExtensions[$object->getExtensionName()])) { + $cloneable = FALSE; + } + + if ($cloneable === NULL) { + foreach (self::$uncloneableClasses as $class) { + if ($original instanceof $class) { + $cloneable = FALSE; + break; + } + } + } + + if ($cloneable === NULL && method_exists($object, 'isCloneable')) { + $cloneable = $object->isCloneable(); + } + + if ($cloneable === NULL && $object->hasMethod('__clone')) { + $method = $object->getMethod('__clone'); + $cloneable = $method->isPublic(); + } + + if ($cloneable === NULL) { + $cloneable = TRUE; + } + + if ($cloneable) { + try { + return clone $original; + } + + catch (Exception $e) { + return $original; + } + } else { + return $original; + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/InvocationMocker.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/InvocationMocker.php new file mode 100644 index 0000000..4a3c04a --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/InvocationMocker.php @@ -0,0 +1,201 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Mocker for invocations which are sent from + * PHPUnit_Framework_MockObject_MockObject objects. + * + * Keeps track of all expectations and stubs as well as registering + * identifications for builders. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_InvocationMocker implements PHPUnit_Framework_MockObject_Stub_MatcherCollection, PHPUnit_Framework_MockObject_Invokable, PHPUnit_Framework_MockObject_Builder_Namespace +{ + /** + * @var PHPUnit_Framework_MockObject_Matcher_Invocation[] + */ + protected $matchers = array(); + + /** + * @var PHPUnit_Framework_MockObject_Builder_Match[] + */ + protected $builderMap = array(); + + /** + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher + */ + public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + $this->matchers[] = $matcher; + } + + /** + * @since Method available since Release 1.1.0 + */ + public function hasMatchers() + { + if (empty($this->matchers)) { + return FALSE; + } + + foreach ($this->matchers as $matcher) { + if (!$matcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount) { + return TRUE; + } + } + + return FALSE; + } + + /** + * @param mixed $id + * @return boolean|null + */ + public function lookupId($id) + { + if (isset($this->builderMap[$id])) { + return $this->builderMap[$id]; + } + + return NULL; + } + + /** + * @param mixed $id + * @param PHPUnit_Framework_MockObject_Builder_Match $builder + * @throws PHPUnit_Framework_Exception + */ + public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder) + { + if (isset($this->builderMap[$id])) { + throw new PHPUnit_Framework_Exception( + 'Match builder with id <' . $id . '> is already registered.' + ); + } + + $this->builderMap[$id] = $builder; + } + + /** + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return new PHPUnit_Framework_MockObject_Builder_InvocationMocker( + $this, $matcher + ); + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return mixed + */ + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + $exception = NULL; + $hasReturnValue = FALSE; + + if (strtolower($invocation->methodName) == '__tostring') { + $returnValue = ''; + } else { + $returnValue = NULL; + } + + foreach ($this->matchers as $match) { + try { + if ($match->matches($invocation)) { + $value = $match->invoked($invocation); + + if (!$hasReturnValue) { + $returnValue = $value; + $hasReturnValue = TRUE; + } + } + } + + catch (Exception $e) { + $exception = $e; + } + } + + if ($exception !== NULL) { + throw $exception; + } + + return $returnValue; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + foreach ($this->matchers as $matcher) { + if (!$matcher->matches($invocation)) { + return FALSE; + } + } + + return TRUE; + } + + /** + * @return boolean + */ + public function verify() + { + foreach ($this->matchers as $matcher) { + $matcher->verify(); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invokable.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invokable.php new file mode 100644 index 0000000..13d3439 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invokable.php @@ -0,0 +1,79 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Interface for classes which can be invoked. + * + * The invocation will be taken from a mock object and passed to an object + * of this class. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Invokable extends PHPUnit_Framework_MockObject_Verifiable +{ + /** + * Invokes the invocation object $invocation so that it can be checked for + * expectations or matched against stubs. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * The invocation object passed from mock object. + * @return object + */ + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation); + + /** + * Checks if the invocation matches. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * The invocation object passed from mock object. + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher.php new file mode 100644 index 0000000..13cc2a0 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher.php @@ -0,0 +1,308 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Main matcher which defines a full expectation using method, parameter and + * invocation matchers. + * This matcher encapsulates all the other matchers and allows the builder to + * set the specific matchers when the appropriate methods are called (once(), + * where() etc.). + * + * All properties are public so that they can easily be accessed by the builder. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher implements PHPUnit_Framework_MockObject_Matcher_Invocation +{ + /** + * @var PHPUnit_Framework_MockObject_Matcher_Invocation + */ + public $invocationMatcher; + + /** + * @var mixed + */ + public $afterMatchBuilderId = NULL; + + /** + * @var boolean + */ + public $afterMatchBuilderIsInvoked = FALSE; + + /** + * @var PHPUnit_Framework_MockObject_Matcher_MethodName + */ + public $methodNameMatcher = NULL; + + /** + * @var PHPUnit_Framework_MockObject_Matcher_Parameters + */ + public $parametersMatcher = NULL; + + /** + * @var PHPUnit_Framework_MockObject_Stub + */ + public $stub = NULL; + + /** + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher + */ + public function __construct(PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher) + { + $this->invocationMatcher = $invocationMatcher; + } + + /** + * @return string + */ + public function toString() + { + $list = array(); + + if ($this->invocationMatcher !== NULL) { + $list[] = $this->invocationMatcher->toString(); + } + + if ($this->methodNameMatcher !== NULL) { + $list[] = 'where ' . $this->methodNameMatcher->toString(); + } + + if ($this->parametersMatcher !== NULL) { + $list[] = 'and ' . $this->parametersMatcher->toString(); + } + + if ($this->afterMatchBuilderId !== NULL) { + $list[] = 'after ' . $this->afterMatchBuilderId; + } + + if ($this->stub !== NULL) { + $list[] = 'will ' . $this->stub->toString(); + } + + return join(' ', $list); + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return mixed + */ + public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation) + { + if ($this->invocationMatcher === NULL) { + throw new PHPUnit_Framework_Exception( + 'No invocation matcher is set' + ); + } + + if ($this->methodNameMatcher === NULL) { + throw new PHPUnit_Framework_Exception('No method matcher is set'); + } + + if ($this->afterMatchBuilderId !== NULL) { + $builder = $invocation->object + ->__phpunit_getInvocationMocker() + ->lookupId($this->afterMatchBuilderId); + + if (!$builder) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'No builder found for match builder identification <%s>', + + $this->afterMatchBuilderId + ) + ); + } + + $matcher = $builder->getMatcher(); + + if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) { + $this->afterMatchBuilderIsInvoked = TRUE; + } + } + + $this->invocationMatcher->invoked($invocation); + + try { + if ( $this->parametersMatcher !== NULL && + !$this->parametersMatcher->matches($invocation)) { + $this->parametersMatcher->verify(); + } + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + throw new PHPUnit_Framework_ExpectationFailedException( + sprintf( + "Expectation failed for %s when %s\n%s", + + $this->methodNameMatcher->toString(), + $this->invocationMatcher->toString(), + $e->getMessage() + ), + $e->getComparisonFailure() + ); + } + + if ($this->stub) { + return $this->stub->invoke($invocation); + } + + return NULL; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + if ($this->afterMatchBuilderId !== NULL) { + $builder = $invocation->object + ->__phpunit_getInvocationMocker() + ->lookupId($this->afterMatchBuilderId); + + if (!$builder) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'No builder found for match builder identification <%s>', + + $this->afterMatchBuilderId + ) + ); + } + + $matcher = $builder->getMatcher(); + + if (!$matcher) { + return FALSE; + } + + if (!$matcher->invocationMatcher->hasBeenInvoked()) { + return FALSE; + } + } + + if ($this->invocationMatcher === NULL) { + throw new PHPUnit_Framework_Exception( + 'No invocation matcher is set' + ); + } + + if ($this->methodNameMatcher === NULL) { + throw new PHPUnit_Framework_Exception('No method matcher is set'); + } + + if (!$this->invocationMatcher->matches($invocation)) { + return FALSE; + } + + try { + if (!$this->methodNameMatcher->matches($invocation)) { + return FALSE; + } + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + throw new PHPUnit_Framework_ExpectationFailedException( + sprintf( + "Expectation failed for %s when %s\n%s", + + $this->methodNameMatcher->toString(), + $this->invocationMatcher->toString(), + $e->getMessage() + ), + $e->getComparisonFailure() + ); + } + + return TRUE; + } + + /** + * @throws PHPUnit_Framework_Exception + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function verify() + { + if ($this->invocationMatcher === NULL) { + throw new PHPUnit_Framework_Exception( + 'No invocation matcher is set' + ); + } + + if ($this->methodNameMatcher === NULL) { + throw new PHPUnit_Framework_Exception('No method matcher is set'); + } + + try { + $this->invocationMatcher->verify(); + + if ($this->parametersMatcher === NULL) { + $this->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters; + } + + $invocationIsAny = get_class($this->invocationMatcher) === 'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount'; + if (!$invocationIsAny) { + $this->parametersMatcher->verify(); + } + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + throw new PHPUnit_Framework_ExpectationFailedException( + sprintf( + "Expectation failed for %s when %s.\n%s", + + $this->methodNameMatcher->toString(), + $this->invocationMatcher->toString(), + $e->getMessage() + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php new file mode 100644 index 0000000..cf86c9e --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php @@ -0,0 +1,72 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which checks if a method has been invoked zero or more + * times. This matcher will always match. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder +{ + /** + * @return string + */ + public function toString() + { + return 'invoked zero or more times'; + } + + /** + */ + public function verify() + { + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php new file mode 100644 index 0000000..5c20f41 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php @@ -0,0 +1,74 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which allos any parameters to a method. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_AnyParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation +{ + /** + * @return string + */ + public function toString() + { + return 'with any parameters'; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + return TRUE; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Invocation.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Invocation.php new file mode 100644 index 0000000..8ffcdcf --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Invocation.php @@ -0,0 +1,88 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Interface for classes which matches an invocation based on its + * method name, argument, order or call count. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Matcher_Invocation extends PHPUnit_Framework_SelfDescribing, PHPUnit_Framework_MockObject_Verifiable +{ + /** + * Registers the invocation $invocation in the object as being invoked. + * This will only occur after matches() returns true which means the + * current invocation is the correct one. + * + * The matcher can store information from the invocation which can later + * be checked in verify(), or it can check the values directly and throw + * and exception if an expectation is not met. + * + * If the matcher is a stub it will also have a return value. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * Object containing information on a mocked or stubbed method which + * was invoked. + * @return mixed + */ + public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation); + + /** + * Checks if the invocation $invocation matches the current rules. If it does + * the matcher will get the invoked() method called which should check if an + * expectation is met. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * Object containing information on a mocked or stubbed method which + * was invoked. + * @return bool + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php new file mode 100644 index 0000000..3026182 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php @@ -0,0 +1,127 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which checks if a method was invoked at a certain index. + * + * If the expected index number does not match the current invocation index it + * will not match which means it skips all method and parameter matching. Only + * once the index is reached will the method and parameter start matching and + * verifying. + * + * If the index is never reached it will throw an exception in index. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex implements PHPUnit_Framework_MockObject_Matcher_Invocation +{ + /** + * @var integer + */ + protected $sequenceIndex; + + /** + * @var integer + */ + protected $currentIndex = -1; + + /** + * @param integer $sequenceIndex + */ + public function __construct($sequenceIndex) + { + $this->sequenceIndex = $sequenceIndex; + } + + /** + * @return string + */ + public function toString() + { + return 'invoked at sequence index ' . $this->sequenceIndex; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + $this->currentIndex++; + + return $this->currentIndex == $this->sequenceIndex; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + */ + public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation) + { + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function verify() + { + if ($this->currentIndex < $this->sequenceIndex) { + throw new PHPUnit_Framework_ExpectationFailedException( + sprintf( + 'The expected invocation at index %s was never reached.', + + $this->sequenceIndex + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php new file mode 100644 index 0000000..2494110 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php @@ -0,0 +1,85 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which checks if a method has been invoked at least one + * time. + * + * If the number of invocations is 0 it will throw an exception in verify. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder +{ + /** + * @return string + */ + public function toString() + { + return 'invoked at least once'; + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function verify() + { + $count = $this->getInvocationCount(); + + if ($count < 1) { + throw new PHPUnit_Framework_ExpectationFailedException( + 'Expected invocation at least once but it never occured.' + ); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php new file mode 100644 index 0000000..3c40018 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php @@ -0,0 +1,143 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which checks if a method has been invoked a certain amount + * of times. + * If the number of invocations exceeds the value it will immediately throw an + * exception, + * If the number is less it will later be checked in verify() and also throw an + * exception. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_InvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder +{ + /** + * @var integer + */ + protected $expectedCount; + + /** + * @param interger $expectedCount + */ + public function __construct($expectedCount) + { + $this->expectedCount = $expectedCount; + } + + /** + * @return string + */ + public function toString() + { + return 'invoked ' . $this->expectedCount . ' time(s)'; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation) + { + parent::invoked($invocation); + + $count = $this->getInvocationCount(); + + if ($count > $this->expectedCount) { + $message = $invocation->toString() . ' '; + + switch ($this->expectedCount) { + case 0: { + $message .= 'was not expected to be called.'; + } + break; + + case 1: { + $message .= 'was not expected to be called more than once.'; + } + break; + + default: { + $message .= sprintf( + 'was not expected to be called more than %d times.', + + $this->expectedCount + ); + } + } + + throw new PHPUnit_Framework_ExpectationFailedException($message); + } + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function verify() + { + $count = $this->getInvocationCount(); + + if ($count !== $this->expectedCount) { + throw new PHPUnit_Framework_ExpectationFailedException( + sprintf( + 'Method was expected to be called %d times, ' . + 'actually called %d times.', + + $this->expectedCount, + $count + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php new file mode 100644 index 0000000..ab6f753 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php @@ -0,0 +1,107 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Records invocations and provides convenience methods for checking them later + * on. + * This abstract class can be implemented by matchers which needs to check the + * number of times an invocation has occured. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + * @abstract + */ +abstract class PHPUnit_Framework_MockObject_Matcher_InvokedRecorder implements PHPUnit_Framework_MockObject_Matcher_Invocation +{ + /** + * @var PHPUnit_Framework_MockObject_Invocation[] + */ + protected $invocations = array(); + + /** + * @return integer + */ + public function getInvocationCount() + { + return count($this->invocations); + } + + /** + * @return PHPUnit_Framework_MockObject_Invocation[] + */ + public function getInvocations() + { + return $this->invocations; + } + + /** + * @return boolean + */ + public function hasBeenInvoked() + { + return count($this->invocations) > 0; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + */ + public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation) + { + $this->invocations[] = $invocation; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + return TRUE; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/MethodName.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/MethodName.php new file mode 100644 index 0000000..58b151e --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/MethodName.php @@ -0,0 +1,102 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which looks for a specific method name in the invocations. + * + * Checks the method name all incoming invocations, the name is checked against + * the defined constraint $constraint. If the constraint is met it will return + * true in matches(). + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_MethodName extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $constraint; + + /** + * @param PHPUnit_Framework_Constraint|string + * @throws PHPUnit_Framework_Constraint + */ + public function __construct($constraint) + { + if (!$constraint instanceof PHPUnit_Framework_Constraint) { + if (!is_string($constraint)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $constraint, 0, 10, FALSE, TRUE + ); + } + + $this->constraint = $constraint; + } + + /** + * @return string + */ + public function toString() + { + return 'method name ' . $this->constraint->toString(); + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + return $this->constraint->evaluate($invocation->methodName, '', TRUE); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Parameters.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Parameters.php new file mode 100644 index 0000000..c2700a7 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Parameters.php @@ -0,0 +1,160 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which looks for specific parameters in the invocations. + * + * Checks the parameters of all incoming invocations, the parameter list is + * checked against the defined constraints in $parameters. If the constraint + * is met it will return true in matches(). + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Matcher_Parameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation +{ + /** + * @var array + */ + protected $parameters = array(); + + /** + * @var PHPUnit_Framework_MockObject_Invocation + */ + protected $invocation; + + /** + * @param array $parameters + */ + public function __construct(array $parameters) + { + foreach ($parameters as $parameter) { + if (!($parameter instanceof PHPUnit_Framework_Constraint)) { + $parameter = new PHPUnit_Framework_Constraint_IsEqual( + $parameter + ); + } + + $this->parameters[] = $parameter; + } + } + + /** + * @return string + */ + public function toString() + { + $text = 'with parameter'; + + foreach ($this->parameters as $index => $parameter) { + if ($index > 0) { + $text .= ' and'; + } + + $text .= ' ' . $index . ' ' . $parameter->toString(); + } + + return $text; + } + + /** + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * @return boolean + */ + public function matches(PHPUnit_Framework_MockObject_Invocation $invocation) + { + $this->invocation = $invocation; + $this->verify(); + + return count($invocation->parameters) < count($this->parameters); + } + + /** + * Checks if the invocation $invocation matches the current rules. If it + * does the matcher will get the invoked() method called which should check + * if an expectation is met. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * Object containing information on a mocked or stubbed method which + * was invoked. + * @return bool + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function verify() + { + if ($this->invocation === NULL) { + throw new PHPUnit_Framework_ExpectationFailedException( + 'Mocked method does not exist.' + ); + } + + if (count($this->invocation->parameters) < count($this->parameters)) { + throw new PHPUnit_Framework_ExpectationFailedException( + sprintf( + 'Parameter count for invocation %s is too low.', + + $this->invocation->toString() + ) + ); + } + + foreach ($this->parameters as $i => $parameter) { + $parameter->evaluate( + $this->invocation->parameters[$i], + sprintf( + 'Parameter %s for invocation %s does not match expected ' . + 'value.', + + $i, + $this->invocation->toString() + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php new file mode 100644 index 0000000..1b3203f --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php @@ -0,0 +1,96 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Invocation matcher which does not care about previous state from earlier + * invocations. + * + * This abstract class can be implemented by matchers which does not care about + * state but only the current run-time value of the invocation itself. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + * @abstract + */ +abstract class PHPUnit_Framework_MockObject_Matcher_StatelessInvocation implements PHPUnit_Framework_MockObject_Matcher_Invocation +{ + /** + * Registers the invocation $invocation in the object as being invoked. + * This will only occur after matches() returns true which means the + * current invocation is the correct one. + * + * The matcher can store information from the invocation which can later + * be checked in verify(), or it can check the values directly and throw + * and exception if an expectation is not met. + * + * If the matcher is a stub it will also have a return value. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * Object containing information on a mocked or stubbed method which + * was invoked. + * @return mixed + */ + public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation) + { + } + + /** + * Checks if the invocation $invocation matches the current rules. If it does + * the matcher will get the invoked() method called which should check if an + * expectation is met. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * Object containing information on a mocked or stubbed method which + * was invoked. + * @return bool + */ + public function verify() + { + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockBuilder.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockBuilder.php new file mode 100644 index 0000000..02a2675 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockBuilder.php @@ -0,0 +1,291 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Giorgio Sironi + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Implementation of the Builder pattern for Mock objects. + * + * @package PHPUnit_MockObject + * @author Giorgio Sironi + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_MockBuilder +{ + /** + * @var PHPUnit_Framework_TestCase + */ + protected $testCase; + + /** + * @var string + */ + protected $className; + + /** + * @var array + */ + protected $methods = array(); + + /** + * @var string + */ + protected $mockClassName = ''; + + /** + * @var array + */ + protected $constructorArgs = array(); + + /** + * @var boolean + */ + protected $originalConstructor = TRUE; + + /** + * @var boolean + */ + protected $originalClone = TRUE; + + /** + * @var boolean + */ + protected $autoload = TRUE; + + /** + * @var boolean + */ + protected $cloneArguments = FALSE; + + /** + * @param PHPUnit_Framework_TestCase + * @param string + */ + public function __construct(PHPUnit_Framework_TestCase $testCase, $className) + { + $this->testCase = $testCase; + $this->className = $className; + } + + /** + * Creates a mock object using a fluent interface. + * + * @return PHPUnit_Framework_MockObject_MockObject + */ + public function getMock() + { + return $this->testCase->getMock( + $this->className, + $this->methods, + $this->constructorArgs, + $this->mockClassName, + $this->originalConstructor, + $this->originalClone, + $this->autoload, + $this->cloneArguments + ); + } + + /** + * Creates a mock object for an abstract class using a fluent interface. + * + * @return PHPUnit_Framework_MockObject_MockObject + */ + public function getMockForAbstractClass() + { + return $this->testCase->getMockForAbstractClass( + $this->className, + $this->constructorArgs, + $this->mockClassName, + $this->originalConstructor, + $this->originalClone, + $this->autoload, + $this->methods, + $this->cloneArguments + ); + } + + /** + * Specifies the subset of methods to mock. Default is to mock all of them. + * + * @param array|null $methods + * @return PHPUnit_Framework_MockObject_MockBuilder + */ + public function setMethods($methods) + { + $this->methods = $methods; + + return $this; + } + + /** + * Specifies the arguments for the constructor. + * + * @param array $args + * @return PHPUnit_Framework_MockObject_MockBuilder + */ + public function setConstructorArgs(array $args) + { + $this->constructorArgs = $args; + + return $this; + } + + /** + * Specifies the name for the mock class. + * + * @param string $name + * @return PHPUnit_Framework_MockObject_MockBuilder + */ + public function setMockClassName($name) + { + $this->mockClassName = $name; + + return $this; + } + + /** + * Disables the invocation of the original constructor. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + */ + public function disableOriginalConstructor() + { + $this->originalConstructor = FALSE; + + return $this; + } + + /** + * Enables the invocation of the original constructor. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + * @since Method available since Release 1.2.0 + */ + public function enableOriginalConstructor() + { + $this->originalConstructor = TRUE; + + return $this; + } + + /** + * Disables the invocation of the original clone constructor. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + */ + public function disableOriginalClone() + { + $this->originalClone = FALSE; + + return $this; + } + + /** + * Enables the invocation of the original clone constructor. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + * @since Method available since Release 1.2.0 + */ + public function enableOriginalClone() + { + $this->originalClone = TRUE; + + return $this; + } + + /** + * Disables the use of class autoloading while creating the mock object. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + */ + public function disableAutoload() + { + $this->autoload = FALSE; + + return $this; + } + + /** + * Enables the use of class autoloading while creating the mock object. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + * @since Method available since Release 1.2.0 + */ + public function enableAutoload() + { + $this->autoload = TRUE; + + return $this; + } + + /** + * Disables the cloning of arguments passed to mocked methods. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + * @since Method available since Release 1.2.0 + */ + public function disableArgumentCloning() + { + $this->cloneArguments = FALSE; + + return $this; + } + + /** + * Enables the cloning of arguments passed to mocked methods. + * + * @return PHPUnit_Framework_MockObject_MockBuilder + * @since Method available since Release 1.2.0 + */ + public function enableArgumentCloning() + { + $this->cloneArguments = TRUE; + + return $this; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockObject.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockObject.php new file mode 100644 index 0000000..20db63b --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockObject.php @@ -0,0 +1,94 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Interface for all mock objects which are generated by + * PHPUnit_Framework_MockObject_Mock. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_MockObject /*extends PHPUnit_Framework_MockObject_Verifiable*/ +{ + /** + * Registers a new expectation in the mock object and returns the match + * object which can be infused with further details. + * + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher); + + /** + * Registers a new static expectation in the mock object and returns the + * match object which can be infused with further details. + * + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher); + + /** + * @return PHPUnit_Framework_MockObject_InvocationMocker + */ + public function __phpunit_getInvocationMocker(); + + /** + * @return PHPUnit_Framework_MockObject_InvocationMocker + */ + public static function __phpunit_getStaticInvocationMocker(); + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function __phpunit_verify(); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub.php new file mode 100644 index 0000000..db3aebc --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub.php @@ -0,0 +1,71 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * An object that stubs the process of a normal method for a mock object. + * + * The stub object will replace the code for the stubbed method and return a + * specific value instead of the original value. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Stub extends PHPUnit_Framework_SelfDescribing +{ + /** + * Fakes the processing of the invocation $invocation by returning a + * specific value. + * + * @param PHPUnit_Framework_MockObject_Invocation $invocation + * The invocation which was mocked and matched by the current method + * and argument matchers. + * @return mixed + */ + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php new file mode 100644 index 0000000..eedd6a1 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php @@ -0,0 +1,87 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Patrick Müller + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Stubs a method by returning a user-defined stack of values. + * + * @package PHPUnit_MockObject + * @author Patrick Müller + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls implements PHPUnit_Framework_MockObject_Stub +{ + protected $stack; + protected $value; + + public function __construct($stack) + { + $this->stack = $stack; + } + + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + $this->value = array_shift($this->stack); + + if ($this->value instanceof PHPUnit_Framework_MockObject_Stub) { + $this->value = $this->value->invoke($invocation); + } + + return $this->value; + } + + public function toString() + { + return sprintf( + 'return user-specified value %s', + + PHPUnit_Util_Type::toString($this->value) + ); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Exception.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Exception.php new file mode 100644 index 0000000..571f9f6 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Exception.php @@ -0,0 +1,80 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Oliver Schlicht + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Stubs a method by raising a user-defined exception. + * + * @package PHPUnit_MockObject + * @author Oliver Schlicht + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Stub_Exception implements PHPUnit_Framework_MockObject_Stub +{ + protected $exception; + + public function __construct(Exception $exception) + { + $this->exception = $exception; + } + + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + throw $this->exception; + } + + public function toString() + { + return sprintf( + 'raise user-specified exception %s', + + PHPUnit_Util_Type::toString($this->exception) + ); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php new file mode 100644 index 0000000..a6c0447 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Stubs a method by returning a user-defined value. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Stub_MatcherCollection +{ + /** + * Adds a new matcher to the collection which can be used as an expectation + * or a stub. + * + * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher + * Matcher for invocations to mock objects. + */ + public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher); +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Return.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Return.php new file mode 100644 index 0000000..bcbfd5e --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Return.php @@ -0,0 +1,78 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Stubs a method by returning a user-defined value. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Stub_Return implements PHPUnit_Framework_MockObject_Stub +{ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + return $this->value; + } + + public function toString() + { + return sprintf( + 'return user-specified value %s', + + PHPUnit_Util_Type::toString($this->value) + ); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php new file mode 100644 index 0000000..0060f81 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php @@ -0,0 +1,78 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Stubs a method by returning an argument that was passed to the mocked method. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Stub_ReturnArgument extends PHPUnit_Framework_MockObject_Stub_Return +{ + protected $argumentIndex; + + public function __construct($argumentIndex) + { + $this->argumentIndex = $argumentIndex; + } + + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + if (isset($invocation->parameters[$this->argumentIndex])) { + return $invocation->parameters[$this->argumentIndex]; + } else { + return NULL; + } + } + + public function toString() + { + return sprintf('return argument #%d', $this->argumentIndex); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php new file mode 100644 index 0000000..c98c4f7 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php @@ -0,0 +1,94 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Framework_MockObject_Stub_ReturnCallback implements PHPUnit_Framework_MockObject_Stub +{ + protected $callback; + + public function __construct($callback) + { + $this->callback = $callback; + } + + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + return call_user_func_array($this->callback, $invocation->parameters); + } + + public function toString() + { + if (is_array($this->callback)) { + if (is_object($this->callback[0])) { + $class = get_class($this->callback[0]); + $type = '->'; + } else { + $class = $this->callback[0]; + $type = '::'; + } + + return sprintf( + 'return result of user defined callback %s%s%s() with the ' . + 'passed arguments', + + $class, + $type, + $this->callback[1] + ); + } else { + return 'return result of user defined callback ' . $this->callback . + ' with the passed arguments'; + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php new file mode 100644 index 0000000..e686c1b --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php @@ -0,0 +1,76 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @author Kris Wallsmith + * @copyright 2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.1.0 + */ + +/** + * Stubs a method by returning the current object. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @author Kris Wallsmith + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.1.0 + */ +class PHPUnit_Framework_MockObject_Stub_ReturnSelf implements PHPUnit_Framework_MockObject_Stub +{ + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + if (!$invocation instanceof PHPUnit_Framework_MockObject_Invocation_Object) { + throw new PHPUnit_Framework_Exception( + 'The current object can only be returned when mocking an ' . + 'object, not a static class.' + ); + } + + return $invocation->object; + } + + public function toString() + { + return 'return the current object'; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php new file mode 100644 index 0000000..5280b33 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php @@ -0,0 +1,87 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.1.0 + */ + +/** + * Stubs a method by returning a value from a map. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Class available since Release 1.1.0 + */ +class PHPUnit_Framework_MockObject_Stub_ReturnValueMap implements PHPUnit_Framework_MockObject_Stub +{ + protected $valueMap; + + public function __construct(array $valueMap) + { + $this->valueMap = $valueMap; + } + + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + $parameterCount = count($invocation->parameters); + + foreach ($this->valueMap as $map) { + if (!is_array($map) || $parameterCount != count($map) - 1) { + continue; + } + + $return = array_pop($map); + if ($invocation->parameters === $map) { + return $return; + } + } + + return NULL; + } + + public function toString() + { + return 'return value from a map'; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Verifiable.php b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Verifiable.php new file mode 100644 index 0000000..77aa492 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Verifiable.php @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +/** + * Interface for classes which must verify a given expectation. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since Interface available since Release 1.0.0 + */ +interface PHPUnit_Framework_MockObject_Verifiable +{ + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function verify(); +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/GeneratorTest.php b/vendor/phpunit/phpunit-mock-objects/Tests/GeneratorTest.php new file mode 100644 index 0000000..1a08df3 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/GeneratorTest.php @@ -0,0 +1,89 @@ +assertTrue(method_exists($mock, 'testFunction')); + } + + /** + * @covers PHPUnit_Framework_MockObject_Generator::getMock + * @expectedException PHPUnit_Framework_Exception + * @expectedExceptionMessage duplicates: "foo, foo" + */ + public function testGetMockGeneratorFails() + { + $mock = PHPUnit_Framework_MockObject_Generator::getMock('StdClass', array('foo', 'foo')); + } + + /** + * @covers PHPUnit_Framework_MockObject_Generator::getObject + */ + public function testMockObjectHasUniqueIdSoThatTwoMockObjectsOfTheSameClassAreNotEqual() + { + $mock1 = PHPUnit_Framework_MockObject_Generator::getMock('stdClass'); + $mock2 = PHPUnit_Framework_MockObject_Generator::getMock('stdClass'); + $this->assertNotEquals($mock1, $mock2); + } + + /** + * @covers PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass + */ + public function testGetMockForAbstractClassDoesNotFailWhenFakingInterfaces() + { + $mock = PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass('Countable'); + $this->assertTrue(method_exists($mock, 'count')); + } + + /** + * @covers PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass + */ + public function testGetMockForAbstractClassStubbingAbstractClass() + { + $mock = PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass('AbstractMockTestClass'); + $this->assertTrue(method_exists($mock, 'doSomething')); + } + + /** + * @dataProvider getMockForAbstractClassExpectsInvalidArgumentExceptionDataprovider + * @covers PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass + * @expectedException PHPUnit_Framework_Exception + */ + public function testGetMockForAbstractClassExpectingInvalidArgumentException($className, $mockClassName) + { + $mock = PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass($className, array(), $mockClassName); + } + + /** + * @covers PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass + * @expectedException PHPUnit_Framework_Exception + */ + public function testGetMockForAbstractClassAnstractClassDoesNotExist() + { + $mock = PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass('Tux'); + } + + /** + * Dataprovider for test "testGetMockForAbstractClassExpectingInvalidArgumentException" + */ + public static function getMockForAbstractClassExpectsInvalidArgumentExceptionDataprovider() + { + return array( + 'className not a string' => array(array(), ''), + 'mockClassName not a string' => array('Countable', new StdClass), + ); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockBuilderTest.php b/vendor/phpunit/phpunit-mock-objects/Tests/MockBuilderTest.php new file mode 100644 index 0000000..1746a31 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockBuilderTest.php @@ -0,0 +1,152 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Giorgio Sironi + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ + +require_once 'PHPUnit/Framework/TestCase.php'; + +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Mockable.php'; + +/** + * @package PHPUnit_MockObject + * @author Giorgio Sironi + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 + */ +class Framework_MockBuilderTest extends PHPUnit_Framework_TestCase +{ + public function testMockBuilderRequiresClassName() + { + $spec = $this->getMockBuilder('Mockable'); + $mock = $spec->getMock(); + $this->assertTrue($mock instanceof Mockable); + } + + public function testByDefaultMocksAllMethods() + { + $spec = $this->getMockBuilder('Mockable'); + $mock = $spec->getMock(); + $this->assertNull($mock->mockableMethod()); + $this->assertNull($mock->anotherMockableMethod()); + } + + public function testMethodsToMockCanBeSpecified() + { + $spec = $this->getMockBuilder('Mockable'); + $spec->setMethods(array('mockableMethod')); + $mock = $spec->getMock(); + $this->assertNull($mock->mockableMethod()); + $this->assertTrue($mock->anotherMockableMethod()); + } + + public function testByDefaultDoesNotPassArgumentsToTheConstructor() + { + $spec = $this->getMockBuilder('Mockable'); + $mock = $spec->getMock(); + $this->assertEquals(array(NULL, NULL), $mock->constructorArgs); + } + + public function testMockClassNameCanBeSpecified() + { + $spec = $this->getMockBuilder('Mockable'); + $spec->setMockClassName('ACustomClassName'); + $mock = $spec->getMock(); + $this->assertTrue($mock instanceof ACustomClassName); + } + + public function testConstructorArgumentsCanBeSpecified() + { + $spec = $this->getMockBuilder('Mockable'); + $spec->setConstructorArgs($expected = array(23, 42)); + $mock = $spec->getMock(); + $this->assertEquals($expected, $mock->constructorArgs); + } + + public function testOriginalConstructorCanBeDisabled() + { + $spec = $this->getMockBuilder('Mockable'); + $spec->disableOriginalConstructor(); + $mock = $spec->getMock(); + $this->assertNull($mock->constructorArgs); + } + + public function testByDefaultOriginalCloneIsPreserved() + { + $spec = $this->getMockBuilder('Mockable'); + $mock = $spec->getMock(); + $cloned = clone $mock; + $this->assertTrue($cloned->cloned); + } + + public function testOriginalCloneCanBeDisabled() + { + $spec = $this->getMockBuilder('Mockable'); + $spec->disableOriginalClone(); + $mock = $spec->getMock(); + $mock->cloned = FALSE; + $cloned = clone $mock; + $this->assertFalse($cloned->cloned); + } + + public function testCallingAutoloadCanBeDisabled() + { + // it is not clear to me how to test this nor the difference + // between calling it or not + $this->markTestIncomplete(); + } + + public function testProvidesAFluentInterface() + { + $spec = $this->getMockBuilder('Mockable') + ->setMethods(array('mockableMethod')) + ->setConstructorArgs(array()) + ->setMockClassName('DummyClassName') + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableAutoload(); + $this->assertTrue($spec instanceof PHPUnit_Framework_MockObject_MockBuilder); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/ObjectTest.php b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/ObjectTest.php new file mode 100644 index 0000000..b28b124 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/ObjectTest.php @@ -0,0 +1,82 @@ +assertSame('FooClass', $invocation->className); + } + + public function testAllowToGetMethodNameSetInConstructor() + { + $invocation = new PHPUnit_Framework_MockObject_Invocation_Object( + 'FooClass', + 'FooMethod', + array('an_argument'), + new StdClass); + + $this->assertSame('FooMethod', $invocation->methodName); + } + + public function testAllowToGetObjectSetInConstructor() + { + $expectedObject = new StdClass; + + $invocation = new PHPUnit_Framework_MockObject_Invocation_Object( + 'FooClass', + 'FooMethod', + array('an_argument'), + $expectedObject); + + $this->assertSame($expectedObject, $invocation->object); + } + + public function testAllowToGetMethodParametersSetInConstructor() + { + $expectedParameters = array( + 'foo', 5, array('a', 'b'), new StdClass, NULL, FALSE + ); + + $invocation = new PHPUnit_Framework_MockObject_Invocation_Object( + 'FooClass', + 'FooMethod', + $expectedParameters, + new StdClass + ); + + $this->assertSame($expectedParameters, $invocation->parameters); + } + + public function testConstructorAllowToSetFlagCloneObjectsInParameters() + { + $parameters = array(new StdClass); + $cloneObjects = TRUE; + + $invocation = new PHPUnit_Framework_MockObject_Invocation_Object( + 'FooClass', + 'FooMethod', + $parameters, + new StdClass, + $cloneObjects + ); + + $this->assertEquals($parameters, $invocation->parameters); + $this->assertNotSame($parameters, $invocation->parameters); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/StaticTest.php b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/StaticTest.php new file mode 100644 index 0000000..09e6e3c --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/Invocation/StaticTest.php @@ -0,0 +1,52 @@ +assertSame('FooClass', $invocation->className); + } + + public function testAllowToGetMethodNameSetInConstructor() + { + $invocation = new PHPUnit_Framework_MockObject_Invocation_Static('FooClass', 'FooMethod', array('an_argument')); + + $this->assertSame('FooMethod', $invocation->methodName); + } + + public function testAllowToGetMethodParametersSetInConstructor() + { + $expectedParameters = array( + 'foo', 5, array('a', 'b'), new StdClass, NULL, FALSE + ); + + $invocation = new PHPUnit_Framework_MockObject_Invocation_Static( + 'FooClass', 'FooMethod', $expectedParameters + ); + + $this->assertSame($expectedParameters, $invocation->parameters); + } + + public function testConstructorAllowToSetFlagCloneObjectsInParameters() + { + $parameters = array(new StdClass); + $cloneObjects = TRUE; + + $invocation = new PHPUnit_Framework_MockObject_Invocation_Static( + 'FooClass', + 'FooMethod', + $parameters, + $cloneObjects + ); + + $this->assertEquals($parameters, $invocation->parameters); + $this->assertNotSame($parameters, $invocation->parameters); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class.phpt new file mode 100644 index 0000000..1df7d97 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class.phpt @@ -0,0 +1,137 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function baz(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'Foo', 'baz', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_clone.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_clone.phpt new file mode 100644 index 0000000..421192e --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_clone.phpt @@ -0,0 +1,91 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + parent::__clone(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_constructor.phpt new file mode 100644 index 0000000..74ae1c7 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_call_parent_constructor.phpt @@ -0,0 +1,90 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_clone.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_clone.phpt new file mode 100644 index 0000000..512400b --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_clone.phpt @@ -0,0 +1,90 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', FALSE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_constructor.phpt new file mode 100644 index 0000000..74ae1c7 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_dont_call_parent_constructor.phpt @@ -0,0 +1,90 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_call_parent_constructor.phpt new file mode 100644 index 0000000..b62504a --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_call_parent_constructor.phpt @@ -0,0 +1,95 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_dont_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_dont_call_parent_constructor.phpt new file mode 100644 index 0000000..b62504a --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_implementing_interface_dont_call_parent_constructor.phpt @@ -0,0 +1,95 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_partial.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_partial.phpt new file mode 100644 index 0000000..c89a714 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/class_partial.phpt @@ -0,0 +1,116 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array('bar'), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/interface.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/interface.phpt new file mode 100644 index 0000000..e322cbe --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/interface.phpt @@ -0,0 +1,110 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo implements PHPUnit_Framework_MockObject_MockObject, Foo +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_object_clone_object.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_object_clone_object.phpt new file mode 100644 index 0000000..9aafafa --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_object_clone_object.phpt @@ -0,0 +1,139 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function baz(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'Foo', 'baz', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_static_clone_object.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_static_clone_object.phpt new file mode 100644 index 0000000..d530091 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/invocation_static_clone_object.phpt @@ -0,0 +1,139 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public static function bar(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = self::__phpunit_getStaticInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Static( + 'Foo', 'bar', $arguments, TRUE + ) + ); + + return $result; + } + + public static function baz(Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = self::__phpunit_getStaticInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Static( + 'Foo', 'baz', $arguments, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class.phpt new file mode 100644 index 0000000..22383ae --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class.phpt @@ -0,0 +1,140 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(NS\Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'NS\Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function baz(NS\Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'NS\Foo', 'baz', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_clone.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_clone.phpt new file mode 100644 index 0000000..d82978c --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_clone.phpt @@ -0,0 +1,93 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + parent::__clone(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_constructor.phpt new file mode 100644 index 0000000..0bbbc71 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_call_parent_constructor.phpt @@ -0,0 +1,92 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_clone.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_clone.phpt new file mode 100644 index 0000000..7829882 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_clone.phpt @@ -0,0 +1,92 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', FALSE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_constructor.phpt new file mode 100644 index 0000000..0bbbc71 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_dont_call_parent_constructor.phpt @@ -0,0 +1,92 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_call_parent_constructor.phpt new file mode 100644 index 0000000..901629c --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_call_parent_constructor.phpt @@ -0,0 +1,97 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt new file mode 100644 index 0000000..901629c --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt @@ -0,0 +1,97 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_partial.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_partial.phpt new file mode 100644 index 0000000..403a14b --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_class_partial.phpt @@ -0,0 +1,118 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array('bar'), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(NS\Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'NS\Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_interface.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_interface.phpt new file mode 100644 index 0000000..f2cf88f --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/namespaced_interface.phpt @@ -0,0 +1,112 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('NS\Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class MockFoo implements PHPUnit_Framework_MockObject_MockObject, NS\Foo +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function bar(NS\Foo $foo) + { + $arguments = array($foo); + $count = func_num_args(); + + if ($count > 1) { + $_arguments = func_get_args(); + + for ($i = 1; $i < $count; $i++) { + $arguments[] = $_arguments[$i]; + } + } + + $result = $this->__phpunit_getInvocationMocker()->invoke( + new PHPUnit_Framework_MockObject_Invocation_Object( + 'NS\Foo', 'bar', $arguments, $this, TRUE + ) + ); + + return $result; + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class.phpt new file mode 100644 index 0000000..01f1a5f --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class.phpt @@ -0,0 +1,87 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +class Foo +{ +} + +class MockFoo extends Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace.phpt new file mode 100644 index 0000000..c05442a --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace.phpt @@ -0,0 +1,95 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +namespace NS { + +class Foo +{ +} + +} + +namespace { + +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace_starting_with_separator.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace_starting_with_separator.phpt new file mode 100644 index 0000000..1f1f535 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/nonexistent_class_with_namespace_starting_with_separator.phpt @@ -0,0 +1,95 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generate('Foo', array(), 'MockFoo', TRUE, TRUE) +--FILE-- + +--EXPECTF-- +namespace NS { + +class Foo +{ +} + +} + +namespace { + +class MockFoo extends NS\Foo implements PHPUnit_Framework_MockObject_MockObject +{ + private static $__phpunit_staticInvocationMocker; + private $__phpunit_invocationMocker; + private $__phpunit_id; + private static $__phpunit_nextId = 0; + + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + $this->__phpunit_setId(); + } + + public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } + + public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher) + { + return self::__phpunit_getStaticInvocationMocker()->expects($matcher); + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === NULL) { + $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return $this->__phpunit_invocationMocker; + } + + public static function __phpunit_getStaticInvocationMocker() + { + if (self::$__phpunit_staticInvocationMocker === NULL) { + self::$__phpunit_staticInvocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker; + } + + return self::$__phpunit_staticInvocationMocker; + } + + public function __phpunit_hasMatchers() + { + return self::__phpunit_getStaticInvocationMocker()->hasMatchers() || + $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify() + { + self::__phpunit_getStaticInvocationMocker()->verify(); + $this->__phpunit_getInvocationMocker()->verify(); + } + + public function __phpunit_cleanup() + { + self::$__phpunit_staticInvocationMocker = NULL; + $this->__phpunit_invocationMocker = NULL; + $this->__phpunit_id = NULL; + } + + public function __phpunit_setId() + { + $this->__phpunit_id = sprintf('%s#%s', get_class($this), self::$__phpunit_nextId++); + } +} + +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class.phpt new file mode 100644 index 0000000..52c9839 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class.phpt @@ -0,0 +1,36 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generateClassFromWsdl('GoogleSearch.wsdl', 'GoogleSearch') +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +class GoogleSearch extends \SOAPClient +{ + public function __construct($wsdl, array $options) + { + parent::__construct('%s/GoogleSearch.wsdl', $options); + } + + public function doGetCachedPage($key, $url) + { + } + + public function doSpellingSuggestion($key, $phrase) + { + } + + public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe) + { + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_namespace.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_namespace.phpt new file mode 100644 index 0000000..b3075c5 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_namespace.phpt @@ -0,0 +1,38 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generateClassFromWsdl('GoogleSearch.wsdl', 'GoogleSearch') +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +namespace My\Space; + +class GoogleSearch extends \SOAPClient +{ + public function __construct($wsdl, array $options) + { + parent::__construct('%s/GoogleSearch.wsdl', $options); + } + + public function doGetCachedPage($key, $url) + { + } + + public function doSpellingSuggestion($key, $phrase) + { + } + + public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe) + { + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_partial.phpt b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_partial.phpt new file mode 100644 index 0000000..4a69f26 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObject/wsdl_class_partial.phpt @@ -0,0 +1,29 @@ +--TEST-- +PHPUnit_Framework_MockObject_Generator::generateClassFromWsdl('GoogleSearch.wsdl', 'GoogleSearch', array('doGoogleSearch')) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +class GoogleSearch extends \SOAPClient +{ + public function __construct($wsdl, array $options) + { + parent::__construct('%s/GoogleSearch.wsdl', $options); + } + + public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe) + { + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/MockObjectTest.php b/vendor/phpunit/phpunit-mock-objects/Tests/MockObjectTest.php new file mode 100644 index 0000000..12b8061 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/MockObjectTest.php @@ -0,0 +1,552 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +require_once 'PHPUnit/Framework/TestCase.php'; + +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'AbstractMockTestClass.php'; +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'AnInterface.php'; +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'FunctionCallback.php'; +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'MethodCallback.php'; +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'PartialMockTestClass.php'; +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'SomeClass.php'; +require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'StaticMockTestClass.php'; + +/** + * + * + * @package PHPUnit_MockObject + * @author Sebastian Bergmann + * @author Patrick Mueller + * @author Frank Kleine + * @copyright 2010-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class Framework_MockObjectTest extends PHPUnit_Framework_TestCase +{ + public function testMockedMethodIsNeverCalled() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->never()) + ->method('doSomething'); + } + + public function testMockedMethodIsNotCalledWhenExpectsAnyWithParameter() + { + $mock = $this->getMock('SomeClass'); + $mock->expects($this->any()) + ->method('doSomethingElse') + ->with('someArg'); + } + + public function testMockedMethodIsCalledAtLeastOnce() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->atLeastOnce()) + ->method('doSomething'); + + $mock->doSomething(); + } + + public function testMockedMethodIsCalledAtLeastOnce2() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->atLeastOnce()) + ->method('doSomething'); + + $mock->doSomething(); + $mock->doSomething(); + } + + public function testMockedMethodIsCalledOnce() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->once()) + ->method('doSomething'); + + $mock->doSomething(); + } + + public function testMockedMethodIsCalledOnceWithParameter() + { + $mock = $this->getMock('SomeClass'); + $mock->expects($this->once()) + ->method('doSomethingElse') + ->with($this->equalTo('something')); + + $mock->doSomethingElse('something'); + } + + public function testMockedMethodIsCalledExactly() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->exactly(2)) + ->method('doSomething'); + + $mock->doSomething(); + $mock->doSomething(); + } + + public function testStubbedException() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->any()) + ->method('doSomething') + ->will($this->throwException(new Exception)); + + try { + $mock->doSomething(); + } + + catch (Exception $e) { + return; + } + + $this->fail(); + } + + public function testStubbedReturnValue() + { + $mock = $this->getMock('AnInterface'); + $mock->expects($this->any()) + ->method('doSomething') + ->will($this->returnValue('something')); + + $this->assertEquals('something', $mock->doSomething()); + } + + public function testStubbedReturnValueMap() + { + $map = array( + array('a', 'b', 'c', 'd'), + array('e', 'f', 'g', 'h') + ); + + $mock = $this->getMock('AnInterface'); + $mock->expects($this->any()) + ->method('doSomething') + ->will($this->returnValueMap($map)); + + $this->assertEquals('d', $mock->doSomething('a', 'b', 'c')); + $this->assertEquals('h', $mock->doSomething('e', 'f', 'g')); + $this->assertEquals(NULL, $mock->doSomething('foo', 'bar')); + } + + public function testFunctionCallback() + { + $mock = $this->getMock('SomeClass', array('doSomething'), array(), '', FALSE); + $mock->expects($this->once()) + ->method('doSomething') + ->will($this->returnCallback('functionCallback')); + + $this->assertEquals('pass', $mock->doSomething('foo', 'bar')); + } + + public function testStaticMethodCallback() + { + $mock = $this->getMock('SomeClass', array('doSomething'), array(), '', FALSE); + $mock->expects($this->once()) + ->method('doSomething') + ->will($this->returnCallback(array('MethodCallback', 'staticCallback'))); + + $this->assertEquals('pass', $mock->doSomething('foo', 'bar')); + } + + public function testPublicMethodCallback() + { + $mock = $this->getMock('SomeClass', array('doSomething'), array(), '', FALSE); + $mock->expects($this->once()) + ->method('doSomething') + ->will($this->returnCallback(array(new MethodCallback, 'nonStaticCallback'))); + + $this->assertEquals('pass', $mock->doSomething('foo', 'bar')); + } + + public function testMockClassOnlyGeneratedOnce() + { + $mock1 = $this->getMock('AnInterface'); + $mock2 = $this->getMock('AnInterface'); + + $this->assertEquals(get_class($mock1), get_class($mock2)); + } + + public function testMockClassDifferentForPartialMocks() + { + $mock1 = $this->getMock('PartialMockTestClass'); + $mock2 = $this->getMock('PartialMockTestClass', array('doSomething')); + $mock3 = $this->getMock('PartialMockTestClass', array('doSomething')); + $mock4 = $this->getMock('PartialMockTestClass', array('doAnotherThing')); + $mock5 = $this->getMock('PartialMockTestClass', array('doAnotherThing')); + + $this->assertNotEquals(get_class($mock1), get_class($mock2)); + $this->assertNotEquals(get_class($mock1), get_class($mock3)); + $this->assertNotEquals(get_class($mock1), get_class($mock4)); + $this->assertNotEquals(get_class($mock1), get_class($mock5)); + $this->assertEquals(get_class($mock2), get_class($mock3)); + $this->assertNotEquals(get_class($mock2), get_class($mock4)); + $this->assertNotEquals(get_class($mock2), get_class($mock5)); + $this->assertEquals(get_class($mock4), get_class($mock5)); + } + + public function testMockClassStoreOverrulable() + { + $mock1 = $this->getMock('PartialMockTestClass'); + $mock2 = $this->getMock('PartialMockTestClass', array(), array(), 'MyMockClassNameForPartialMockTestClass1'); + $mock3 = $this->getMock('PartialMockTestClass'); + $mock4 = $this->getMock('PartialMockTestClass', array('doSomething'), array(), 'AnotherMockClassNameForPartialMockTestClass'); + $mock5 = $this->getMock('PartialMockTestClass', array(), array(), 'MyMockClassNameForPartialMockTestClass2'); + + $this->assertNotEquals(get_class($mock1), get_class($mock2)); + $this->assertEquals(get_class($mock1), get_class($mock3)); + $this->assertNotEquals(get_class($mock1), get_class($mock4)); + $this->assertNotEquals(get_class($mock2), get_class($mock3)); + $this->assertNotEquals(get_class($mock2), get_class($mock4)); + $this->assertNotEquals(get_class($mock2), get_class($mock5)); + $this->assertNotEquals(get_class($mock3), get_class($mock4)); + $this->assertNotEquals(get_class($mock3), get_class($mock5)); + $this->assertNotEquals(get_class($mock4), get_class($mock5)); + } + + /** + * @covers PHPUnit_Framework_MockObject_Generator::getMock + */ + public function testGetMockWithFixedClassNameCanProduceTheSameMockTwice() + { + $mock = $this->getMockBuilder('StdClass')->setMockClassName('FixedName')->getMock(); + $mock = $this->getMockBuilder('StdClass')->setMockClassName('FixedName')->getMock(); + $this->assertInstanceOf('StdClass', $mock); + } + + public function testOriginalConstructorSettingConsidered() + { + $mock1 = $this->getMock('PartialMockTestClass'); + $mock2 = $this->getMock('PartialMockTestClass', array(), array(), '', FALSE); + + $this->assertTrue($mock1->constructorCalled); + $this->assertFalse($mock2->constructorCalled); + } + + public function testOriginalCloneSettingConsidered() + { + $mock1 = $this->getMock('PartialMockTestClass'); + $mock2 = $this->getMock('PartialMockTestClass', array(), array(), '', TRUE, FALSE); + + $this->assertNotEquals(get_class($mock1), get_class($mock2)); + } + + public function testStubbedReturnValueForStaticMethod() + { + $this->getMockClass( + 'StaticMockTestClass', + array('doSomething'), + array(), + 'StaticMockTestClassMock' + ); + + StaticMockTestClassMock::staticExpects($this->any()) + ->method('doSomething') + ->will($this->returnValue('something')); + + $this->assertEquals( + 'something', StaticMockTestClassMock::doSomething() + ); + } + + public function testStubbedReturnValueForStaticMethod2() + { + $this->getMockClass( + 'StaticMockTestClass', + array('doSomething'), + array(), + 'StaticMockTestClassMock2' + ); + + StaticMockTestClassMock2::staticExpects($this->any()) + ->method('doSomething') + ->will($this->returnValue('something')); + + $this->assertEquals( + 'something', StaticMockTestClassMock2::doSomethingElse() + ); + } + + public function testGetMockForAbstractClass() + { + $mock = $this->getMock('AbstractMockTestClass'); + $mock->expects($this->never()) + ->method('doSomething'); + } + + public function testStaticMethodCallWithArgumentCloningEnabled() + { + $expectedObject = new StdClass; + + $this->getMockClass( + 'StaticMockTestClass', + array('doSomething'), + array(), + 'StaticMockTestClassMock3', + FALSE, + TRUE, + TRUE, + TRUE + ); + + $actualArguments = array(); + + StaticMockTestClassMock3::staticExpects($this->any()) + ->method('doSomething') + ->will($this->returnCallback(function() use (&$actualArguments) { + $actualArguments = func_get_args(); + })); + + StaticMockTestClassMock3::doSomething($expectedObject); + + $this->assertEquals(1, count($actualArguments)); + $this->assertNotSame($expectedObject, $actualArguments[0]); + } + + public function testStaticMethodCallWithArgumentCloningDisabled() + { + $expectedObject = new StdClass; + + $this->getMockClass( + 'StaticMockTestClass', + array('doSomething'), + array(), + 'StaticMockTestClassMock4', + FALSE, + TRUE, + TRUE, + FALSE + ); + + $actualArguments = array(); + + StaticMockTestClassMock4::staticExpects($this->any()) + ->method('doSomething') + ->will($this->returnCallback(function() use (&$actualArguments) { + $actualArguments = func_get_args(); + })); + + StaticMockTestClassMock4::doSomething($expectedObject); + + $this->assertEquals(1, count($actualArguments)); + $this->assertEquals($expectedObject, $actualArguments[0]); + $this->assertSame($expectedObject, $actualArguments[0]); + } + + public function testObjectMethodCallWithArgumentCloningEnabled() + { + $expectedObject = new StdClass; + + $mock = $this->getMockBuilder('SomeClass') + ->setMethods(array('doSomethingElse')) + ->enableArgumentCloning() + ->getMock(); + + $actualArguments = array(); + + $mock->expects($this->any()) + ->method('doSomethingElse') + ->will($this->returnCallback(function() use (&$actualArguments) { + $actualArguments = func_get_args(); + })); + + $mock->doSomethingElse($expectedObject); + + $this->assertEquals(1, count($actualArguments)); + $this->assertEquals($expectedObject, $actualArguments[0]); + $this->assertNotSame($expectedObject, $actualArguments[0]); + } + + public function testObjectMethodCallWithArgumentCloningDisabled() + { + $expectedObject = new StdClass; + + $mock = $this->getMockBuilder('SomeClass') + ->setMethods(array('doSomethingElse')) + ->disableArgumentCloning() + ->getMock(); + + $actualArguments = array(); + + $mock->expects($this->any()) + ->method('doSomethingElse') + ->will($this->returnCallback(function() use (&$actualArguments) { + $actualArguments = func_get_args(); + })); + + $mock->doSomethingElse($expectedObject); + + $this->assertEquals(1, count($actualArguments)); + $this->assertNotSame($expectedObject, $actualArguments[0]); + } + + public function testVerificationOfMethodNameFailsWithoutParameters() + { + $mock = $this->getMock('SomeClass', array('right', 'wrong'), array(), '', TRUE, TRUE, TRUE); + $mock->expects($this->once()) + ->method('right'); + + $mock->wrong(); + try { + $mock->__phpunit_verify(); + $this->fail('Expected exception'); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertSame( + "Expectation failed for method name is equal to when invoked 1 time(s).\n" + . 'Method was expected to be called 1 times, actually called 0 times.', + $e->getMessage() + ); + } + + $this->resetMockObjects(); + } + + public function testVerificationOfMethodNameFailsWithParameters() + { + $mock = $this->getMock('SomeClass', array('right', 'wrong'), array(), '', TRUE, TRUE, TRUE); + $mock->expects($this->once()) + ->method('right'); + + $mock->wrong(); + try { + $mock->__phpunit_verify(); + $this->fail('Expected exception'); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertSame( + "Expectation failed for method name is equal to when invoked 1 time(s).\n" + . 'Method was expected to be called 1 times, actually called 0 times.', + $e->getMessage() + ); + } + + $this->resetMockObjects(); + } + + public function testVerificationOfNeverFailsWithEmptyParameters() + { + $mock = $this->getMock('SomeClass', array('right', 'wrong'), array(), '', TRUE, TRUE, TRUE); + $mock->expects($this->never()) + ->method('right') + ->with(); + + try { + $mock->right(); + $this->fail('Expected exception'); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertSame( + 'SomeClass::right() was not expected to be called.', + $e->getMessage() + ); + } + + $this->resetMockObjects(); + } + + public function testVerificationOfNeverFailsWithAnyParameters() + { + $mock = $this->getMock('SomeClass', array('right', 'wrong'), array(), '', TRUE, TRUE, TRUE); + $mock->expects($this->never()) + ->method('right') + ->withAnyParameters(); + + try { + $mock->right(); + $this->fail('Expected exception'); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertSame( + 'SomeClass::right() was not expected to be called.', + $e->getMessage() + ); + } + + $this->resetMockObjects(); + } + + /** + * @requires extension soap + */ + public function testCreateMockFromWsdl() + { + $mock = $this->getMockFromWsdl(__DIR__ . '/_files/GoogleSearch.wsdl', 'WsdlMock'); + $this->assertStringStartsWith( + 'Mock_WsdlMock_', + get_class($mock) + ); + } + + /** + * @requires extension soap + */ + public function testCreateNamespacedMockFromWsdl() + { + $mock = $this->getMockFromWsdl(__DIR__ . '/_files/GoogleSearch.wsdl', 'My\\Space\\WsdlMock'); + $this->assertStringStartsWith( + 'Mock_WsdlMock_', + get_class($mock) + ); + } + + /** + * @requires extension soap + */ + public function testCreateTwoMocksOfOneWsdlFile() + { + $mock = $this->getMockFromWsdl(__DIR__ . '/_files/GoogleSearch.wsdl'); + $mock = $this->getMockFromWsdl(__DIR__ . '/_files/GoogleSearch.wsdl'); + } + + private function resetMockObjects() + { + $refl = new ReflectionObject($this); + $refl = $refl->getParentClass(); + $prop = $refl->getProperty('mockObjects'); + $prop->setAccessible(true); + $prop->setValue($this, array()); + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/_files/AbstractMockTestClass.php b/vendor/phpunit/phpunit-mock-objects/Tests/_files/AbstractMockTestClass.php new file mode 100644 index 0000000..1f2641f --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/_files/AbstractMockTestClass.php @@ -0,0 +1,5 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/_files/MethodCallback.php b/vendor/phpunit/phpunit-mock-objects/Tests/_files/MethodCallback.php new file mode 100644 index 0000000..f8db62e --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/_files/MethodCallback.php @@ -0,0 +1,21 @@ +constructorArgs = array($arg1, $arg2); + } + + public function mockableMethod() + { + // something different from NULL + return TRUE; + } + + public function anotherMockableMethod() + { + // something different from NULL + return TRUE; + } + + public function __clone() + { + $this->cloned = TRUE; + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/_files/PartialMockTestClass.php b/vendor/phpunit/phpunit-mock-objects/Tests/_files/PartialMockTestClass.php new file mode 100644 index 0000000..1b119f1 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/_files/PartialMockTestClass.php @@ -0,0 +1,18 @@ +constructorCalled = TRUE; + } + + public function doSomething() + { + } + + public function doAnotherThing() + { + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/Tests/_files/SomeClass.php b/vendor/phpunit/phpunit-mock-objects/Tests/_files/SomeClass.php new file mode 100644 index 0000000..2cd53b2 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/Tests/_files/SomeClass.php @@ -0,0 +1,13 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/phpunit-mock-objects/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php b/vendor/phpunit/phpunit-mock-objects/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php new file mode 100644 index 0000000..bf9d520 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/build/PHPCS/Sniffs/ControlStructures/ControlSignatureSniff.php @@ -0,0 +1,22 @@ +getTokens(); + + if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE || + $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { + + $phpcsFile->addError( + 'Concatenation operator must be surrounded by whitespace', + $stackPtr + ); + } + } +} diff --git a/vendor/phpunit/phpunit-mock-objects/build/PHPCS/ruleset.xml b/vendor/phpunit/phpunit-mock-objects/build/PHPCS/ruleset.xml new file mode 100644 index 0000000..402f214 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/build/PHPCS/ruleset.xml @@ -0,0 +1,35 @@ + + + Sebastian Bergmann's coding standard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/phpunit-mock-objects/build/phpmd.xml b/vendor/phpunit/phpunit-mock-objects/build/phpmd.xml new file mode 100644 index 0000000..23ecb8b --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/build/phpmd.xml @@ -0,0 +1,27 @@ + + + + Sebastian Bergmann's ruleset + + + + + + + + + + + + + + + + + + + diff --git a/vendor/phpunit/phpunit-mock-objects/composer.json b/vendor/phpunit/phpunit-mock-objects/composer.json new file mode 100644 index 0000000..4ed97c1 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/composer.json @@ -0,0 +1,41 @@ +{ + "name": "phpunit/phpunit-mock-objects", + "description": "Mock Object library for PHPUnit", + "type": "library", + "keywords": [ + "xunit", + "mock" + ], + "homepage": "http://www.phpunit.de/", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "version": "1.2.1", + "time": "2012-10-05", + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable", + "ext-reflection": "*", + "ext-spl": "*" + }, + "suggest": { + "ext-soap": "*" + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "include-path": [ + "" + ] +} diff --git a/vendor/phpunit/phpunit-mock-objects/package-composer.json b/vendor/phpunit/phpunit-mock-objects/package-composer.json new file mode 100644 index 0000000..bff1cf4 --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/package-composer.json @@ -0,0 +1,19 @@ +{ + "name": "phpunit/phpunit-mock-objects", + "keywords": [ "xunit", "mock" ], + "license": "BSD-3-Clause", + "homepage": "http://www.phpunit.de/", + "dependency_map": { + "pear.phpunit.de/Text_Template": "phpunit/php-text-template" + }, + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", + "irc": "irc://irc.freenode.net/phpunit" + }, + "autoload": { + "classmap": [ "PHPUnit/" ] + }, + "include_path": [ + "" + ] +} diff --git a/vendor/phpunit/phpunit-mock-objects/package.xml b/vendor/phpunit/phpunit-mock-objects/package.xml new file mode 100644 index 0000000..85248ad --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/package.xml @@ -0,0 +1,199 @@ + + + PHPUnit_MockObject + pear.phpunit.de + Mock Object library for PHPUnit + Mock Object library for PHPUnit + + Sebastian Bergmann + sb + sb@sebastian-bergmann.de + yes + + 2012-10-05 + + 1.2.1 + 1.2.0 + + + stable + stable + + The BSD 3-Clause License + http://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/README.markdown + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.9.4 + + + Text_Template + pear.phpunit.de + 1.1.1 + + + reflection + + + spl + + + + + soap + + + + + diff --git a/vendor/phpunit/phpunit-mock-objects/phpunit.xml.dist b/vendor/phpunit/phpunit-mock-objects/phpunit.xml.dist new file mode 100644 index 0000000..0ce22df --- /dev/null +++ b/vendor/phpunit/phpunit-mock-objects/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + Tests + Tests + + + + + + + + + + + + PHPUnit + + PHPUnit/Framework/MockObject/Autoload.php + + + + diff --git a/vendor/phpunit/phpunit/CONTRIBUTING.md b/vendor/phpunit/phpunit/CONTRIBUTING.md new file mode 100644 index 0000000..156d2f0 --- /dev/null +++ b/vendor/phpunit/phpunit/CONTRIBUTING.md @@ -0,0 +1,55 @@ +Contributing to PHPUnit +======================= + +Contributions to PHPUnit, its related modules, and its documentation are always welcome. You make our lifes easier by sending us your contributions through GitHub pull requests. + +Please note that the `3.6.` branch is closed for features and that pull requests should to be based on `master` or the `3.7.` once it exists. + +We are trying to keep backwards compatibility breaks in PHPUnit 3.7 to an absolute minimum so please take this into account when proposing changes. + +Due to time constraints, we are not always able to respond as quickly as we would like. Please do not take delays personal and feel free to remind us here or on IRC if you feel that we forgot to respond. + +Using PHPUnit From a Git Checkout +--------------------------------- + +The following commands can be used to perform the initial checkout of PHPUnit and its dependencies from Git: + + mkdir phpunit && cd phpunit + git clone git://github.com/sebastianbergmann/phpunit.git + git clone git://github.com/sebastianbergmann/dbunit.git + git clone git://github.com/sebastianbergmann/php-file-iterator.git + git clone git://github.com/sebastianbergmann/php-text-template.git + git clone git://github.com/sebastianbergmann/php-code-coverage.git + git clone git://github.com/sebastianbergmann/php-token-stream.git + git clone git://github.com/sebastianbergmann/php-timer.git + git clone git://github.com/sebastianbergmann/phpunit-mock-objects.git + git clone git://github.com/sebastianbergmann/phpunit-selenium.git + git clone git://github.com/sebastianbergmann/phpunit-story.git + git clone git://github.com/sebastianbergmann/php-invoker.git + +The `dbunit`, `php-code-coverage`, `php-file-iterator`, `php-text-template`, `php-timer`, `php-token-stream`, `phpunit`, `phpunit-mock-objects`, `phpunit-selenium`, `phpunit-story`, and `php-invoker` directories need to be added to the `include_path`. + +In addition to the checkouts listed above, the YAML component that is provided by the Symfony project is required: + + pear install pear.symfony.com/Yaml + +The `phpunit/phpunit.php` script can be used to invoke the PHPUnit test runner. + +Running the test suite(s) +------------------------- + +It is not possible to use a system-wide installed version of PHPUnit to run the test suite of a Git checkout. Because of that is is necessary to change the `include_paths` as described below. + +This can be achieved with a small wrapper script designed to work with every module in the PHPUnit stack. + +Note that you might have to change the path to your PEAR installation here pointing to `/usr/local/lib/php`. You can find it using `pear config-show | grep php_dir`. + +### Linux / MacOS X + + #!/bin/bash + php -d include_path='.:../phpunit/:../dbunit/:../php-code-coverage/:../php-file-iterator/:../php-invoker/:../php-text-template/:../php-timer:../php-token-stream:../phpunit-mock-objects/:../phpunit-selenium/:../phpunit-story/:/usr/local/lib/php' ../phpunit/phpunit.php $* + +### Windows + + @echo off + php -d include_path='.;../phpunit/;../dbunit/;../php-code-coverage/;../php-file-iterator/;../php-invoker/;../php-text-template/;../php-timer;../php-token-stream;../phpunit-mock-objects/;../phpunit-selenium/;../phpunit-story/;C:/Program Files/PHP/pear' ../phpunit/phpunit.php %* diff --git a/vendor/phpunit/phpunit/ChangeLog.md b/vendor/phpunit/phpunit/ChangeLog.md new file mode 100644 index 0000000..01b7741 --- /dev/null +++ b/vendor/phpunit/phpunit/ChangeLog.md @@ -0,0 +1,82 @@ +PHPUnit 3.7 +=========== + +This is the list of changes for the PHPUnit 3.7 release series. + +PHPUnit 3.7.8 +------------- + +* Fixed #688: Invoke autoloader when checking for `Symfony\Component\Yaml\Dumper`. + +PHPUnit 3.7.7 +------------- + +* Added missing file to PEAR package. + +PHPUnit 3.7.6 +------------- + +* Fixed #682: `phpunit` script appears in stacktrace (when PHPUnit is installed through Composer). + +PHPUnit 3.7.5 +------------- + +* PHPUnit now uses `$_SERVER['SCRIPT_NAME']` instead of `$_SERVER['_']` to filter the `phpunit` script (as the latter is not set when PHPUnit is invoked from Apache Ant's `` task, for instance). + +PHPUnit 3.7.4 +------------- + +* Fixed #682: `phpunit` script appears in stacktrace. + +PHPUnit 3.7.3 +------------- + +* Improvements to running PHPUnit from a PHAR. + +PHPUnit 3.7.2 +------------- + +* Implemented #656: Always clean up mock objects (and free up memory). +* Implemented #664: Do not rely on autoloader class map to populate blacklist. +* Added the `addUncoveredFilesFromWhitelist` configuration setting back in. +* Fixed #655: Reverted 'More than two positional arguments provided' check as it turned out to be a BC issue. +* Disable token caching (in PHP_TokenStream, used by PHP_CodeCoverage) by default (to reduce memory footprint). + +PHPUnit 3.7.1 +------------- + +* The version number is now displayed when using PHPUnit from a Composer install or Git checkout. + +PHPUnit 3.7.0 +------------- + +* PHPUnit 3.7 is only supported on PHP 5.3.3 (or later) and PHP 5.4.7 (or later) is highly recommended. +* Implemented #200: When using process-isolation don't die silently when unserializing the test result fails. +* Implemented #206: Added a `callback` constraint that is useful for making complex assertions. +* Implemented #207: Restore current working directory if is changed by a test case. +* Implemented #208: Added `--test-suffix` that allows specifying which filename suffixes are recognised by PHPUnit. +* Implemented #295: `assertArrayHasKey()` and `assertArrayNotHasKey()` now work with objects that implement ArrayAccess. +* Implemented #333: Improved reporting when there are unused CLI arguments to avoid misconceptions. +* Implemented #377: Show messages and stracktraces in JSON output for skipped and incomplete tests. +* Implemented #424: Added `assertJson*` functions that work like the existing `assertXml*` functions. +* Implemented #492: PHPUnit now provides a `configuration.xsd` schema file at [http://schema.phpunit.de/configuration.xsd]() that can be used to validate your `phpunit.xml` and `phpunit.xml.dist` configuration files. +* Implemented #504: Expanded the `@requires` annotation to allow for checking the existence of functions and extensions using multiple `@requires function name` statements. +* Implemented #508 #86: `@expectedExceptionCode` and `@expectedExceptionMessage` can now use constants like `Classname::CONST` as their parameters. They will get evaluated if the class constant exists and used for comparison so test authors can avoid duplication. +* Implemented #512: Test listeners now trigger one autoload call instead of being silently ignored when the class was not loaded. +* Implemented #514: Failed `assertStringMatchesFormat()` calls now produce a better readable diff by only marking lines as different that don't match the format specifiers. +* Implemented #515: Added `assertContainsOnlyInstancesOf()` to help checking Collection objects and arrays with a descriptive assertion. +* Implemented #561: When an `@expectedException` fails it now shows the message of the thrown exception to ease debugging. +* Implemented #586: Improved reporting of exceptions by printing out the previous exception names, messages and traces. +* The `@requires` annotation can now be used on the class DocBlock. Required versions can be overridden in the methods annotation, required functions and extensions will be merged. +* Added `processUncoveredFilesFromWhitelist` configuration setting. When enabled, uncovered whitelisted files are processed to properly calculate the number of executable lines. +* Fixed #322 #320 thanks to #607: Commandline option now override group/exclude settings in `phpunit.xml` +* Fixed #440: Possible crash when using `--process-isolation` with PHP 5.3 and `detect_unicode=on`. +* Fixed #523: `assertAttributeEquals()` now works with classes extending internal classes like `ArrayIterator`. +* Fixed #581: Generating a diffs could add extra newlines in Windows. +* Fixed #636, #631: Using Selenium in combination with autoloaders that `die()` or produce errors when a class cannot be found caused broken tests. +* If no tests where executed, for example because of a `--filter`, PHPUnit now prints a "No tests executed" warning instead of "OK (0 tests...)". +* It is possible again to expect the generic `Exception` class. +* Removed `addUncoveredFilesFromWhitelist` configuration setting. +* Removed deprecated `--skeleton-class` and `--skeleton-test` switches. The functionality is now provided by the `phpunit-skelgen` command of the `PHPUnit_SkeletonGenerator` package. +* Removed deprecated `PHPUnit_Extensions_OutputTestCase` class. + diff --git a/vendor/phpunit/phpunit/LICENSE b/vendor/phpunit/phpunit/LICENSE new file mode 100644 index 0000000..a701552 --- /dev/null +++ b/vendor/phpunit/phpunit/LICENSE @@ -0,0 +1,33 @@ +PHPUnit + +Copyright (c) 2002-2012, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/phpunit/phpunit/PHPUnit/Autoload.php b/vendor/phpunit/phpunit/PHPUnit/Autoload.php new file mode 100644 index 0000000..a775c1d --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Autoload.php @@ -0,0 +1,208 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */ + +require_once 'File/Iterator/Autoload.php'; +require_once 'PHP/CodeCoverage/Autoload.php'; +require_once 'PHP/Timer/Autoload.php'; +require_once 'PHPUnit/Framework/MockObject/Autoload.php'; +require_once 'Text/Template/Autoload.php'; + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + 'phpunit_extensions_grouptestsuite' => '/Extensions/GroupTestSuite.php', + 'phpunit_extensions_phpttestcase' => '/Extensions/PhptTestCase.php', + 'phpunit_extensions_phpttestcase_logger' => '/Extensions/PhptTestCase/Logger.php', + 'phpunit_extensions_phpttestsuite' => '/Extensions/PhptTestSuite.php', + 'phpunit_extensions_repeatedtest' => '/Extensions/RepeatedTest.php', + 'phpunit_extensions_testdecorator' => '/Extensions/TestDecorator.php', + 'phpunit_extensions_ticketlistener' => '/Extensions/TicketListener.php', + 'phpunit_framework_assert' => '/Framework/Assert.php', + 'phpunit_framework_assertionfailederror' => '/Framework/AssertionFailedError.php', + 'phpunit_framework_comparator' => '/Framework/Comparator.php', + 'phpunit_framework_comparator_array' => '/Framework/Comparator/Array.php', + 'phpunit_framework_comparator_domdocument' => '/Framework/Comparator/DOMDocument.php', + 'phpunit_framework_comparator_double' => '/Framework/Comparator/Double.php', + 'phpunit_framework_comparator_exception' => '/Framework/Comparator/Exception.php', + 'phpunit_framework_comparator_mockobject' => '/Framework/Comparator/MockObject.php', + 'phpunit_framework_comparator_numeric' => '/Framework/Comparator/Numeric.php', + 'phpunit_framework_comparator_object' => '/Framework/Comparator/Object.php', + 'phpunit_framework_comparator_resource' => '/Framework/Comparator/Resource.php', + 'phpunit_framework_comparator_scalar' => '/Framework/Comparator/Scalar.php', + 'phpunit_framework_comparator_splobjectstorage' => '/Framework/Comparator/SplObjectStorage.php', + 'phpunit_framework_comparator_type' => '/Framework/Comparator/Type.php', + 'phpunit_framework_comparatorfactory' => '/Framework/ComparatorFactory.php', + 'phpunit_framework_comparisonfailure' => '/Framework/ComparisonFailure.php', + 'phpunit_framework_constraint' => '/Framework/Constraint.php', + 'phpunit_framework_constraint_and' => '/Framework/Constraint/And.php', + 'phpunit_framework_constraint_arrayhaskey' => '/Framework/Constraint/ArrayHasKey.php', + 'phpunit_framework_constraint_attribute' => '/Framework/Constraint/Attribute.php', + 'phpunit_framework_constraint_callback' => '/Framework/Constraint/Callback.php', + 'phpunit_framework_constraint_classhasattribute' => '/Framework/Constraint/ClassHasAttribute.php', + 'phpunit_framework_constraint_classhasstaticattribute' => '/Framework/Constraint/ClassHasStaticAttribute.php', + 'phpunit_framework_constraint_composite' => '/Framework/Constraint/Composite.php', + 'phpunit_framework_constraint_count' => '/Framework/Constraint/Count.php', + 'phpunit_framework_constraint_exception' => '/Framework/Constraint/Exception.php', + 'phpunit_framework_constraint_exceptioncode' => '/Framework/Constraint/ExceptionCode.php', + 'phpunit_framework_constraint_exceptionmessage' => '/Framework/Constraint/ExceptionMessage.php', + 'phpunit_framework_constraint_fileexists' => '/Framework/Constraint/FileExists.php', + 'phpunit_framework_constraint_greaterthan' => '/Framework/Constraint/GreaterThan.php', + 'phpunit_framework_constraint_isanything' => '/Framework/Constraint/IsAnything.php', + 'phpunit_framework_constraint_isempty' => '/Framework/Constraint/IsEmpty.php', + 'phpunit_framework_constraint_isequal' => '/Framework/Constraint/IsEqual.php', + 'phpunit_framework_constraint_isfalse' => '/Framework/Constraint/IsFalse.php', + 'phpunit_framework_constraint_isidentical' => '/Framework/Constraint/IsIdentical.php', + 'phpunit_framework_constraint_isinstanceof' => '/Framework/Constraint/IsInstanceOf.php', + 'phpunit_framework_constraint_isnull' => '/Framework/Constraint/IsNull.php', + 'phpunit_framework_constraint_istrue' => '/Framework/Constraint/IsTrue.php', + 'phpunit_framework_constraint_istype' => '/Framework/Constraint/IsType.php', + 'phpunit_framework_constraint_jsonmatches' => '/Framework/Constraint/JsonMatches.php', + 'phpunit_framework_constraint_jsonmatches_errormessageprovider' => '/Framework/Constraint/JsonMatches/ErrorMessageProvider.php', + 'phpunit_framework_constraint_lessthan' => '/Framework/Constraint/LessThan.php', + 'phpunit_framework_constraint_not' => '/Framework/Constraint/Not.php', + 'phpunit_framework_constraint_objecthasattribute' => '/Framework/Constraint/ObjectHasAttribute.php', + 'phpunit_framework_constraint_or' => '/Framework/Constraint/Or.php', + 'phpunit_framework_constraint_pcrematch' => '/Framework/Constraint/PCREMatch.php', + 'phpunit_framework_constraint_samesize' => '/Framework/Constraint/SameSize.php', + 'phpunit_framework_constraint_stringcontains' => '/Framework/Constraint/StringContains.php', + 'phpunit_framework_constraint_stringendswith' => '/Framework/Constraint/StringEndsWith.php', + 'phpunit_framework_constraint_stringmatches' => '/Framework/Constraint/StringMatches.php', + 'phpunit_framework_constraint_stringstartswith' => '/Framework/Constraint/StringStartsWith.php', + 'phpunit_framework_constraint_traversablecontains' => '/Framework/Constraint/TraversableContains.php', + 'phpunit_framework_constraint_traversablecontainsonly' => '/Framework/Constraint/TraversableContainsOnly.php', + 'phpunit_framework_constraint_xor' => '/Framework/Constraint/Xor.php', + 'phpunit_framework_error' => '/Framework/Error.php', + 'phpunit_framework_error_deprecated' => '/Framework/Error/Deprecated.php', + 'phpunit_framework_error_notice' => '/Framework/Error/Notice.php', + 'phpunit_framework_error_warning' => '/Framework/Error/Warning.php', + 'phpunit_framework_exception' => '/Framework/Exception.php', + 'phpunit_framework_expectationfailedexception' => '/Framework/ExpectationFailedException.php', + 'phpunit_framework_incompletetest' => '/Framework/IncompleteTest.php', + 'phpunit_framework_incompletetesterror' => '/Framework/IncompleteTestError.php', + 'phpunit_framework_outputerror' => '/Framework/OutputError.php', + 'phpunit_framework_selfdescribing' => '/Framework/SelfDescribing.php', + 'phpunit_framework_skippedtest' => '/Framework/SkippedTest.php', + 'phpunit_framework_skippedtesterror' => '/Framework/SkippedTestError.php', + 'phpunit_framework_skippedtestsuiteerror' => '/Framework/SkippedTestSuiteError.php', + 'phpunit_framework_syntheticerror' => '/Framework/SyntheticError.php', + 'phpunit_framework_test' => '/Framework/Test.php', + 'phpunit_framework_testcase' => '/Framework/TestCase.php', + 'phpunit_framework_testfailure' => '/Framework/TestFailure.php', + 'phpunit_framework_testlistener' => '/Framework/TestListener.php', + 'phpunit_framework_testresult' => '/Framework/TestResult.php', + 'phpunit_framework_testsuite' => '/Framework/TestSuite.php', + 'phpunit_framework_testsuite_dataprovider' => '/Framework/TestSuite/DataProvider.php', + 'phpunit_framework_warning' => '/Framework/Warning.php', + 'phpunit_runner_basetestrunner' => '/Runner/BaseTestRunner.php', + 'phpunit_runner_standardtestsuiteloader' => '/Runner/StandardTestSuiteLoader.php', + 'phpunit_runner_testsuiteloader' => '/Runner/TestSuiteLoader.php', + 'phpunit_runner_version' => '/Runner/Version.php', + 'phpunit_textui_command' => '/TextUI/Command.php', + 'phpunit_textui_resultprinter' => '/TextUI/ResultPrinter.php', + 'phpunit_textui_testrunner' => '/TextUI/TestRunner.php', + 'phpunit_util_class' => '/Util/Class.php', + 'phpunit_util_configuration' => '/Util/Configuration.php', + 'phpunit_util_deprecatedfeature' => '/Util/DeprecatedFeature.php', + 'phpunit_util_deprecatedfeature_logger' => '/Util/DeprecatedFeature/Logger.php', + 'phpunit_util_diff' => '/Util/Diff.php', + 'phpunit_util_errorhandler' => '/Util/ErrorHandler.php', + 'phpunit_util_fileloader' => '/Util/Fileloader.php', + 'phpunit_util_filesystem' => '/Util/Filesystem.php', + 'phpunit_util_filter' => '/Util/Filter.php', + 'phpunit_util_getopt' => '/Util/Getopt.php', + 'phpunit_util_globalstate' => '/Util/GlobalState.php', + 'phpunit_util_invalidargumenthelper' => '/Util/InvalidArgumentHelper.php', + 'phpunit_util_log_json' => '/Util/Log/JSON.php', + 'phpunit_util_log_junit' => '/Util/Log/JUnit.php', + 'phpunit_util_log_tap' => '/Util/Log/TAP.php', + 'phpunit_util_php' => '/Util/PHP.php', + 'phpunit_util_php_default' => '/Util/PHP/Default.php', + 'phpunit_util_php_windows' => '/Util/PHP/Windows.php', + 'phpunit_util_printer' => '/Util/Printer.php', + 'phpunit_util_string' => '/Util/String.php', + 'phpunit_util_test' => '/Util/Test.php', + 'phpunit_util_testdox_nameprettifier' => '/Util/TestDox/NamePrettifier.php', + 'phpunit_util_testdox_resultprinter' => '/Util/TestDox/ResultPrinter.php', + 'phpunit_util_testdox_resultprinter_html' => '/Util/TestDox/ResultPrinter/HTML.php', + 'phpunit_util_testdox_resultprinter_text' => '/Util/TestDox/ResultPrinter/Text.php', + 'phpunit_util_testsuiteiterator' => '/Util/TestSuiteIterator.php', + 'phpunit_util_type' => '/Util/Type.php', + 'phpunit_util_xml' => '/Util/XML.php' + ); + + $path = dirname(__FILE__); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); + +if (stream_resolve_include_path('PHP/Invoker/Autoload.php')) { + require_once 'PHP/Invoker/Autoload.php'; +} + +if (stream_resolve_include_path('PHPUnit/Extensions/Database/Autoload.php')) { + require_once 'PHPUnit/Extensions/Database/Autoload.php'; +} + +if (stream_resolve_include_path('PHPUnit/Extensions/SeleniumCommon/Autoload.php')) { + require_once 'PHPUnit/Extensions/SeleniumCommon/Autoload.php'; +} + +else if (stream_resolve_include_path('PHPUnit/Extensions/SeleniumTestCase/Autoload.php')) { + require_once 'PHPUnit/Extensions/SeleniumTestCase/Autoload.php'; +} + +if (stream_resolve_include_path('PHPUnit/Extensions/Story/Autoload.php')) { + require_once 'PHPUnit/Extensions/Story/Autoload.php'; +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Autoload.php.in b/vendor/phpunit/phpunit/PHPUnit/Autoload.php.in new file mode 100644 index 0000000..1d16b94 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Autoload.php.in @@ -0,0 +1,91 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */ + +require_once 'File/Iterator/Autoload.php'; +require_once 'PHP/CodeCoverage/Autoload.php'; +require_once 'PHP/Timer/Autoload.php'; +require_once 'PHPUnit/Framework/MockObject/Autoload.php'; +require_once 'Text/Template/Autoload.php'; + +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; + + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(__FILE__); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); + +if (stream_resolve_include_path('PHP/Invoker/Autoload.php')) { + require_once 'PHP/Invoker/Autoload.php'; +} + +if (stream_resolve_include_path('PHPUnit/Extensions/Database/Autoload.php')) { + require_once 'PHPUnit/Extensions/Database/Autoload.php'; +} + +if (stream_resolve_include_path('PHPUnit/Extensions/SeleniumCommon/Autoload.php')) { + require_once 'PHPUnit/Extensions/SeleniumCommon/Autoload.php'; +} + +else if (stream_resolve_include_path('PHPUnit/Extensions/SeleniumTestCase/Autoload.php')) { + require_once 'PHPUnit/Extensions/SeleniumTestCase/Autoload.php'; +} + +if (stream_resolve_include_path('PHPUnit/Extensions/Story/Autoload.php')) { + require_once 'PHPUnit/Extensions/Story/Autoload.php'; +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/GroupTestSuite.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/GroupTestSuite.php new file mode 100644 index 0000000..80f8360 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/GroupTestSuite.php @@ -0,0 +1,99 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Extensions + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * We have a TestSuite object A. + * In TestSuite object A we have Tests tagged with @group. + * We want a TestSuite object B that contains TestSuite objects C, D, ... + * for the Tests tagged with @group C, @group D, ... + * Running the Tests from TestSuite object B results in Tests tagged with both + * @group C and @group D in TestSuite object A to be run twice . + * + * + * $suite = new PHPUnit_Extensions_GroupTestSuite($A, array('C', 'D')); + * + * + * @package PHPUnit + * @subpackage Extensions + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Extensions_GroupTestSuite extends PHPUnit_Framework_TestSuite +{ + public function __construct(PHPUnit_Framework_TestSuite $suite, array $groups) + { + $groupSuites = array(); + $name = $suite->getName(); + + foreach ($groups as $group) { + $groupSuites[$group] = new PHPUnit_Framework_TestSuite($name . ' - ' . $group); + $this->addTest($groupSuites[$group]); + } + + $tests = new RecursiveIteratorIterator( + new PHPUnit_Util_TestSuiteIterator($suite), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($tests as $test) { + if ($test instanceof PHPUnit_Framework_TestCase) { + $testGroups = PHPUnit_Util_Test::getGroups( + get_class($test), $test->getName(FALSE) + ); + + foreach ($groups as $group) { + foreach ($testGroups as $testGroup) { + if ($group == $testGroup) { + $groupSuites[$group]->addTest($test); + } + } + } + } + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase.php new file mode 100644 index 0000000..16336bc --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase.php @@ -0,0 +1,269 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Extensions_PhptTestCase + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.4 + */ + +if (stream_resolve_include_path('PEAR/RunTest.php')) { + $currentErrorReporting = error_reporting(E_ERROR | E_WARNING | E_PARSE); + require_once 'PEAR/RunTest.php'; + error_reporting($currentErrorReporting); +} + +/** + * Wrapper to run .phpt test cases. + * + * @package PHPUnit + * @subpackage Extensions_PhptTestCase + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Extensions_PhptTestCase implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing +{ + /** + * The filename of the .phpt file. + * + * @var string + */ + protected $filename; + + /** + * Options for PEAR_RunTest. + * + * @var array + */ + protected $options = array(); + + /** + * Constructs a test case with the given filename. + * + * @param string $filename + * @param array $options + */ + public function __construct($filename, array $options = array()) + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_file($filename)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'File "%s" does not exist.', + $filename + ) + ); + } + + $this->filename = $filename; + $this->options = $options; + } + + /** + * Counts the number of test cases executed by run(TestResult result). + * + * @return integer + */ + public function count() + { + return 1; + } + + /** + * Runs a test and collects its result in a TestResult instance. + * + * @param PHPUnit_Framework_TestResult $result + * @param array $options + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = NULL, array $options = array()) + { + if (!class_exists('PEAR_RunTest', FALSE)) { + throw new PHPUnit_Framework_Exception('Class PEAR_RunTest not found.'); + } + + if (isset($GLOBALS['_PEAR_destructor_object_list']) && + is_array($GLOBALS['_PEAR_destructor_object_list']) && + !empty($GLOBALS['_PEAR_destructor_object_list'])) { + $pearDestructorObjectListCount = count($GLOBALS['_PEAR_destructor_object_list']); + } else { + $pearDestructorObjectListCount = 0; + } + + if ($result === NULL) { + $result = new PHPUnit_Framework_TestResult; + } + + $coverage = $result->getCollectCodeCoverageInformation(); + $options = array_merge($options, $this->options); + + if (!isset($options['include_path'])) { + $options['include_path'] = get_include_path(); + } + + if ($coverage) { + $options['coverage'] = TRUE; + } else { + $options['coverage'] = FALSE; + } + + $currentErrorReporting = error_reporting(E_ERROR | E_WARNING | E_PARSE); + $runner = new PEAR_RunTest(new PHPUnit_Extensions_PhptTestCase_Logger, $options); + + if ($coverage) { + $runner->xdebug_loaded = TRUE; + } else { + $runner->xdebug_loaded = FALSE; + } + + $result->startTest($this); + + PHP_Timer::start(); + + $buffer = $runner->run($this->filename, $options); + $time = PHP_Timer::stop(); + + error_reporting($currentErrorReporting); + + $base = basename($this->filename); + $path = dirname($this->filename); + $coverageFile = $path . DIRECTORY_SEPARATOR . str_replace( + '.phpt', '.xdebug', $base + ); + $diffFile = $path . DIRECTORY_SEPARATOR . str_replace( + '.phpt', '.diff', $base + ); + $expFile = $path . DIRECTORY_SEPARATOR . str_replace( + '.phpt', '.exp', $base + ); + $logFile = $path . DIRECTORY_SEPARATOR . str_replace( + '.phpt', '.log', $base + ); + $outFile = $path . DIRECTORY_SEPARATOR . str_replace( + '.phpt', '.out', $base + ); + $phpFile = $path . DIRECTORY_SEPARATOR . str_replace( + '.phpt', '.php', $base + ); + + if (is_object($buffer) && $buffer instanceof PEAR_Error) { + $result->addError( + $this, + new PHPUnit_Framework_Exception($buffer->getMessage()), + $time + ); + } + + else if ($buffer == 'SKIPPED') { + $result->addFailure($this, new PHPUnit_Framework_SkippedTestError, 0); + } + + else if ($buffer != 'PASSED') { + $expContent = file_get_contents($expFile); + $outContent = file_get_contents($outFile); + + $result->addFailure( + $this, + new PHPUnit_Framework_ComparisonFailure( + $expContent, + $outContent, + $expContent, + $outContent + ), + $time + ); + } + + foreach (array($diffFile, $expFile, $logFile, $phpFile, $outFile) as $file) { + if (file_exists($file)) { + unlink($file); + } + } + + if ($coverage && file_exists($coverageFile)) { + eval('$coverageData = ' . file_get_contents($coverageFile) . ';'); + unset($coverageData[$phpFile]); + + $result->getCodeCoverage()->append($coverageData, $this); + unlink($coverageFile); + } + + $result->endTest($this, $time); + + // Do not invoke PEAR's destructor mechanism for PHP 4 + // as it raises an E_STRICT. + if ($pearDestructorObjectListCount == 0) { + unset($GLOBALS['_PEAR_destructor_object_list']); + } else { + $count = count($GLOBALS['_PEAR_destructor_object_list']) - $pearDestructorObjectListCount; + + for ($i = 0; $i < $count; $i++) { + array_pop($GLOBALS['_PEAR_destructor_object_list']); + } + } + + return $result; + } + + /** + * Returns the name of the test case. + * + * @return string + */ + public function getName() + { + return $this->toString(); + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + return $this->filename; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase/Logger.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase/Logger.php new file mode 100644 index 0000000..6b33a41 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase/Logger.php @@ -0,0 +1,62 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Extensions_PhptTestCase + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.4 + */ + +/** + * Dummy logger for PEAR_RunTest. + * + * @package PHPUnit + * @subpackage Extensions_PhptTestCase + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Extensions_PhptTestCase_Logger +{ + public function log($level, $msg, $append_crlf = TRUE) + { + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestSuite.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestSuite.php new file mode 100644 index 0000000..ad394f2 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/PhptTestSuite.php @@ -0,0 +1,82 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Extensions_PhptTestCase + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.4 + */ + +/** + * Suite for .phpt test cases. + * + * @package PHPUnit + * @subpackage Extensions_PhptTestCase + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Extensions_PhptTestSuite extends PHPUnit_Framework_TestSuite +{ + /** + * Constructs a new TestSuite for .phpt test cases. + * + * @param string $directory + * @param array $options Array with ini settings for the php instance run, + * key being the name if the setting, value the ini value. + * @throws PHPUnit_Framework_Exception + */ + public function __construct($directory, array $options = array()) + { + if (is_string($directory) && is_dir($directory)) { + $this->setName($directory); + + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray($directory, '.phpt'); + + foreach ($files as $file) { + $this->addTestFile($file, $options); + } + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'directory name'); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/RepeatedTest.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/RepeatedTest.php new file mode 100644 index 0000000..261520b --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/RepeatedTest.php @@ -0,0 +1,155 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A Decorator that runs a test repeatedly. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator +{ + /** + * @var mixed + */ + protected $filter = FALSE; + + /** + * @var array + */ + protected $groups = array(); + + /** + * @var array + */ + protected $excludeGroups = array(); + + /** + * @var boolean + */ + protected $processIsolation = FALSE; + + /** + * @var integer + */ + protected $timesRepeat = 1; + + /** + * Constructor. + * + * @param PHPUnit_Framework_Test $test + * @param integer $timesRepeat + * @param mixed $filter + * @param array $groups + * @param array $excludeGroups + * @param boolean $processIsolation + * @throws PHPUnit_Framework_Exception + */ + public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $filter = FALSE, array $groups = array(), array $excludeGroups = array(), $processIsolation = FALSE) + { + parent::__construct($test); + + if (is_integer($timesRepeat) && + $timesRepeat >= 0) { + $this->timesRepeat = $timesRepeat; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, 'positive integer' + ); + } + + $this->filter = $filter; + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + $this->processIsolation = $processIsolation; + } + + /** + * Counts the number of test cases that + * will be run by this test. + * + * @return integer + */ + public function count() + { + return $this->timesRepeat * count($this->test); + } + + /** + * Runs the decorated test and collects the + * result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + * @return PHPUnit_Framework_TestResult + * @throws PHPUnit_Framework_Exception + */ + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + if ($result === NULL) { + $result = $this->createResult(); + } + + //@codingStandardsIgnoreStart + for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) { + //@codingStandardsIgnoreEnd + if ($this->test instanceof PHPUnit_Framework_TestSuite) { + $this->test->run( + $result, + $this->filter, + $this->groups, + $this->excludeGroups, + $this->processIsolation + ); + } else { + $this->test->run($result); + } + } + + return $result; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/TestDecorator.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/TestDecorator.php new file mode 100644 index 0000000..8bdfb16 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/TestDecorator.php @@ -0,0 +1,149 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Extensions + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A Decorator for Tests. + * + * Use TestDecorator as the base class for defining new + * test decorators. Test decorator subclasses can be introduced + * to add behaviour before or after a test is run. + * + * @package PHPUnit + * @subpackage Extensions + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Extensions_TestDecorator extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing +{ + /** + * The Test to be decorated. + * + * @var object + */ + protected $test = NULL; + + /** + * Constructor. + * + * @param PHPUnit_Framework_Test $test + */ + public function __construct(PHPUnit_Framework_Test $test) + { + $this->test = $test; + } + + /** + * Returns a string representation of the test. + * + * @return string + */ + public function toString() + { + return $this->test->toString(); + } + + /** + * Runs the test and collects the + * result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + */ + public function basicRun(PHPUnit_Framework_TestResult $result) + { + $this->test->run($result); + } + + /** + * Counts the number of test cases that + * will be run by this test. + * + * @return integer + */ + public function count() + { + return count($this->test); + } + + /** + * Creates a default TestResult object. + * + * @return PHPUnit_Framework_TestResult + */ + protected function createResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * Returns the test to be run. + * + * @return PHPUnit_Framework_Test + */ + public function getTest() + { + return $this->test; + } + + /** + * Runs the decorated test and collects the + * result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + if ($result === NULL) { + $result = $this->createResult(); + } + + $this->basicRun($result); + + return $result; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Extensions/TicketListener.php b/vendor/phpunit/phpunit/PHPUnit/Extensions/TicketListener.php new file mode 100644 index 0000000..4be38e1 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Extensions/TicketListener.php @@ -0,0 +1,224 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Extensions_TicketListener + * @author Sean Coates + * @author Raphael Stolt + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Base class for test listeners that interact with an issue tracker. + * + * @package PHPUnit + * @subpackage Extensions_TicketListener + * @author Sean Coates + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +abstract class PHPUnit_Extensions_TicketListener implements PHPUnit_Framework_TestListener +{ + /** + * @var array + */ + protected $ticketCounts = array(); + + /** + * @var boolean + */ + protected $ran = FALSE; + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if (!$test instanceof PHPUnit_Framework_Warning) { + if ($this->ran) { + return; + } + + $name = $test->getName(FALSE); + $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name); + + foreach ($tickets as $ticket) { + $this->ticketCounts[$ticket][$name] = 1; + } + + $this->ran = TRUE; + } + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if (!$test instanceof PHPUnit_Framework_Warning) { + if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + $ifStatus = array('assigned', 'new', 'reopened'); + $newStatus = 'closed'; + $message = 'Automatically closed by PHPUnit (test passed).'; + $resolution = 'fixed'; + $cumulative = TRUE; + } + + else if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) { + $ifStatus = array('closed'); + $newStatus = 'reopened'; + $message = 'Automatically reopened by PHPUnit (test failed).'; + $resolution = ''; + $cumulative = FALSE; + } + + else { + return; + } + + $name = $test->getName(FALSE); + $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name); + + foreach ($tickets as $ticket) { + // Remove this test from the totals (if it passed). + if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + unset($this->ticketCounts[$ticket][$name]); + } + + // Only close tickets if ALL referenced cases pass + // but reopen tickets if a single test fails. + if ($cumulative) { + // Determine number of to-pass tests: + if (count($this->ticketCounts[$ticket]) > 0) { + // There exist remaining test cases with this reference. + $adjustTicket = FALSE; + } else { + // No remaining tickets, go ahead and adjust. + $adjustTicket = TRUE; + } + } else { + $adjustTicket = TRUE; + } + + $ticketInfo = $this->getTicketInfo($ticket); + + if ($adjustTicket && in_array($ticketInfo['status'], $ifStatus)) { + $this->updateTicket($ticket, $newStatus, $message, $resolution); + } + } + } + } + + abstract protected function getTicketInfo($ticketId = NULL); + abstract protected function updateTicket($ticketId, $newStatus, $message, $resolution); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Assert.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Assert.php new file mode 100644 index 0000000..66e2301 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Assert.php @@ -0,0 +1,2832 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A set of assert methods. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +abstract class PHPUnit_Framework_Assert +{ + /** + * @var integer + */ + private static $count = 0; + + /** + * Asserts that an array has a specified key. + * + * @param mixed $key + * @param array|ArrayAccess $array + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertArrayHasKey($key, $array, $message = '') + { + if (!(is_integer($key) || is_string($key))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'integer or string' + ); + } + if (!(is_array($array) || $array instanceof ArrayAccess)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'array or ArrayAccess' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_ArrayHasKey($key); + + self::assertThat($array, $constraint, $message); + } + + /** + * Asserts that an array does not have a specified key. + * + * @param mixed $key + * @param array|ArrayAccess $array + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertArrayNotHasKey($key, $array, $message = '') + { + if (!(is_integer($key) || is_string($key))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'integer or string' + ); + } + if (!(is_array($array) || $array instanceof ArrayAccess)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'array or ArrayAccess' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ArrayHasKey($key) + ); + + self::assertThat($array, $constraint, $message); + } + + /** + * Asserts that a haystack contains a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 2.1.0 + */ + public static function assertContains($needle, $haystack, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) + { + if (is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable) { + $constraint = new PHPUnit_Framework_Constraint_TraversableContains( + $needle, $checkForObjectIdentity + ); + } + + else if (is_string($haystack)) { + $constraint = new PHPUnit_Framework_Constraint_StringContains( + $needle, $ignoreCase + ); + } + + else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, 'array, iterator or string' + ); + } + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 3.0.0 + */ + public static function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) + { + self::assertContains( + $needle, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message, + $ignoreCase, + $checkForObjectIdentity + ); + } + + /** + * Asserts that a haystack does not contain a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 2.1.0 + */ + public static function assertNotContains($needle, $haystack, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) + { + if (is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable) { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_TraversableContains( + $needle, $checkForObjectIdentity + ) + ); + } + + else if (is_string($haystack)) { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringContains( + $needle, $ignoreCase + ) + ); + } + + else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, 'array, iterator or string' + ); + } + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 3.0.0 + */ + public static function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) + { + self::assertNotContains( + $needle, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message, + $ignoreCase, + $checkForObjectIdentity + ); + } + + /** + * Asserts that a haystack contains only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ + public static function assertContainsOnly($type, $haystack, $isNativeType = NULL, $message = '') + { + if (!(is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, 'array or iterator' + ); + } + + if ($isNativeType == NULL) { + $isNativeType = PHPUnit_Util_Type::isType($type); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_TraversableContainsOnly( + $type, $isNativeType + ), + $message + ); + } + + /** + * Asserts that a haystack contains only instances of a given classname + * + * @param string $classname + * @param array|Traversable $haystack + * @param string $message + */ + public static function assertContainsOnlyInstancesOf($classname, $haystack, $message = '') + { + if (!(is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, 'array or iterator' + ); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_TraversableContainsOnly( + $classname, FALSE + ), + $message + ); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains only values of a given type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ + public static function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = NULL, $message = '') + { + self::assertContainsOnly( + $type, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $isNativeType, + $message + ); + } + + /** + * Asserts that a haystack does not contain only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ + public static function assertNotContainsOnly($type, $haystack, $isNativeType = NULL, $message = '') + { + if (!(is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, 'array or iterator' + ); + } + + if ($isNativeType == NULL) { + $isNativeType = PHPUnit_Util_Type::isType($type); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_TraversableContainsOnly( + $type, $isNativeType + ) + ), + $message + ); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain only values of a given + * type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ + public static function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = NULL, $message = '') + { + self::assertNotContainsOnly( + $type, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $isNativeType, + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Iterator. + * + * @param integer $expectedCount + * @param mixed $haystack + * @param string $message + */ + public static function assertCount($expectedCount, $haystack, $message = '') + { + if (!is_int($expectedCount)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + if (!$haystack instanceof Countable && + !$haystack instanceof Iterator && + !is_array($haystack)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable'); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_Count($expectedCount), + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Iterator + * that is stored in an attribute. + * + * @param integer $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.6.0 + */ + public static function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertCount( + $expectedCount, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Iterator. + * + * @param integer $expectedCount + * @param mixed $haystack + * @param string $message + */ + public static function assertNotCount($expectedCount, $haystack, $message = '') + { + if (!is_int($expectedCount)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + if (!$haystack instanceof Countable && + !$haystack instanceof Iterator && + !is_array($haystack)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_Count($expectedCount) + ); + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts the number of elements of an array, Countable or Iterator + * that is stored in an attribute. + * + * @param integer $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.6.0 + */ + public static function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertNotCount( + $expectedCount, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that two variables are equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ + public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $expected, $delta, $maxDepth, $canonicalize, $ignoreCase + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a variable is equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ + public static function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + self::assertEquals( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that two variables are not equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 2.3.0 + */ + public static function assertNotEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsEqual( + $expected, $delta, $maxDepth, $canonicalize, $ignoreCase + ) + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a variable is not equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ + public static function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + self::assertNotEquals( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that a variable is empty. + * + * @param mixed $actual + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertEmpty($actual, $message = '') + { + self::assertThat($actual, self::isEmpty(), $message); + } + + /** + * Asserts that a static attribute of a class or an attribute of an object + * is empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertEmpty( + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that a variable is not empty. + * + * @param mixed $actual + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertNotEmpty($actual, $message = '') + { + self::assertThat($actual, self::logicalNot(self::isEmpty()), $message); + } + + /** + * Asserts that a static attribute of a class or an attribute of an object + * is not empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertNotEmpty( + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that a value is greater than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertGreaterThan($expected, $actual, $message = '') + { + self::assertThat($actual, self::greaterThan($expected), $message); + } + + /** + * Asserts that an attribute is greater than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertGreaterThan( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is greater than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertGreaterThanOrEqual($expected, $actual, $message = '') + { + self::assertThat( + $actual, self::greaterThanOrEqual($expected), $message + ); + } + + /** + * Asserts that an attribute is greater than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertGreaterThanOrEqual( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is smaller than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertLessThan($expected, $actual, $message = '') + { + self::assertThat($actual, self::lessThan($expected), $message); + } + + /** + * Asserts that an attribute is smaller than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertLessThan( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is smaller than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertLessThanOrEqual($expected, $actual, $message = '') + { + self::assertThat($actual, self::lessThanOrEqual($expected), $message); + } + + /** + * Asserts that an attribute is smaller than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertLessThanOrEqual( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.2.14 + */ + public static function assertFileEquals($expected, $actual, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) + { + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); + + self::assertEquals( + file_get_contents($expected), + file_get_contents($actual), + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.2.14 + */ + public static function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) + { + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); + + self::assertNotEquals( + file_get_contents($expected), + file_get_contents($actual), + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of a string is equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.3.0 + */ + public static function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) + { + self::assertFileExists($expectedFile, $message); + + self::assertEquals( + file_get_contents($expectedFile), + $actualString, + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.3.0 + */ + public static function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) + { + self::assertFileExists($expectedFile, $message); + + self::assertNotEquals( + file_get_contents($expectedFile), + $actualString, + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that a file exists. + * + * @param string $filename + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertFileExists($filename, $message = '') + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_FileExists; + + self::assertThat($filename, $constraint, $message); + } + + /** + * Asserts that a file does not exist. + * + * @param string $filename + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertFileNotExists($filename, $message = '') + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_FileExists + ); + + self::assertThat($filename, $constraint, $message); + } + + /** + * Asserts that a condition is true. + * + * @param boolean $condition + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertTrue($condition, $message = '') + { + self::assertThat($condition, self::isTrue(), $message); + } + + /** + * Asserts that a condition is false. + * + * @param boolean $condition + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertFalse($condition, $message = '') + { + self::assertThat($condition, self::isFalse(), $message); + } + + /** + * Asserts that a variable is not NULL. + * + * @param mixed $actual + * @param string $message + */ + public static function assertNotNull($actual, $message = '') + { + self::assertThat($actual, self::logicalNot(self::isNull()), $message); + } + + /** + * Asserts that a variable is NULL. + * + * @param mixed $actual + * @param string $message + */ + public static function assertNull($actual, $message = '') + { + self::assertThat($actual, self::isNull(), $message); + } + + /** + * Asserts that a class has a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertClassHasAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($className) || !class_exists($className, FALSE)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name'); + } + + $constraint = new PHPUnit_Framework_Constraint_ClassHasAttribute( + $attributeName + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that a class does not have a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertClassNotHasAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($className) || !class_exists($className, FALSE)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ClassHasAttribute($attributeName) + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that a class has a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertClassHasStaticAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($className) || !class_exists($className, FALSE)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name'); + } + + $constraint = new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( + $attributeName + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that a class does not have a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertClassNotHasStaticAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($className) || !class_exists($className, FALSE)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( + $attributeName + ) + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that an object has a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertObjectHasAttribute($attributeName, $object, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_object($object)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object'); + } + + $constraint = new PHPUnit_Framework_Constraint_ObjectHasAttribute( + $attributeName + ); + + self::assertThat($object, $constraint, $message); + } + + /** + * Asserts that an object does not have a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertObjectNotHasAttribute($attributeName, $object, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_object($object)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ObjectHasAttribute($attributeName) + ); + + self::assertThat($object, $constraint, $message); + } + + /** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ + public static function assertSame($expected, $actual, $message = '') + { + if (is_bool($expected) && is_bool($actual)) { + self::assertEquals($expected, $actual, $message); + } else { + $constraint = new PHPUnit_Framework_Constraint_IsIdentical( + $expected + ); + + self::assertThat($actual, $constraint, $message); + } + } + + /** + * Asserts that a variable and an attribute of an object have the same type + * and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ + public static function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertSame( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ + public static function assertNotSame($expected, $actual, $message = '') + { + if (is_bool($expected) && is_bool($actual)) { + self::assertNotEquals($expected, $actual, $message); + } else { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsIdentical($expected) + ); + + self::assertThat($actual, $constraint, $message); + } + } + + /** + * Asserts that a variable and an attribute of an object do not have the + * same type and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ + public static function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertNotSame( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertInstanceOf($expected, $actual, $message = '') + { + if (is_string($expected)) { + if (class_exists($expected) || interface_exists($expected)) { + $constraint = new PHPUnit_Framework_Constraint_IsInstanceOf( + $expected + ); + } + + else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'class or interface name' + ); + } + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '') + { + self::assertInstanceOf( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertNotInstanceOf($expected, $actual, $message = '') + { + if (is_string($expected)) { + if (class_exists($expected) || interface_exists($expected)) { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsInstanceOf($expected) + ); + } + + else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'class or interface name' + ); + } + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '') + { + self::assertNotInstanceOf( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertInternalType($expected, $actual, $message = '') + { + if (is_string($expected)) { + $constraint = new PHPUnit_Framework_Constraint_IsType( + $expected + ); + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '') + { + self::assertInternalType( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertNotInternalType($expected, $actual, $message = '') + { + if (is_string($expected)) { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsType($expected) + ); + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '') + { + self::assertNotInternalType( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a string matches a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + */ + public static function assertRegExp($pattern, $string, $message = '') + { + if (!is_string($pattern)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_PCREMatch($pattern); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string does not match a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + * @since Method available since Release 2.1.0 + */ + public static function assertNotRegExp($pattern, $string, $message = '') + { + if (!is_string($pattern)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_PCREMatch($pattern) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Assert that the size of two arrays (or `Countable` or `Iterator` objects) + * is the same. + * + * @param array|Countable|Iterator $expected + * @param array|Countable|Iterator $actual + * @param string $message + */ + public static function assertSameSize($expected, $actual, $message = '') + { + if (!$expected instanceof Countable && + !$expected instanceof Iterator && + !is_array($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable'); + } + + if (!$actual instanceof Countable && + !$actual instanceof Iterator && + !is_array($actual)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable'); + } + + self::assertThat( + $actual, + new PHPUnit_Framework_Constraint_SameSize($expected), + $message + ); + } + + /** + * Assert that the size of two arrays (or `Countable` or `Iterator` objects) + * is not the same. + * + * @param array|Countable|Iterator $expected + * @param array|Countable|Iterator $actual + * @param string $message + */ + public static function assertNotSameSize($expected, $actual, $message = '') + { + if (!$expected instanceof Countable && + !$expected instanceof Iterator && + !is_array($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable'); + } + + if (!$actual instanceof Countable && + !$actual instanceof Iterator && + !is_array($actual)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_SameSize($expected) + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a string matches a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertStringMatchesFormat($format, $string, $message = '') + { + if (!is_string($format)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringMatches($format); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string does not match a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertStringNotMatchesFormat($format, $string, $message = '') + { + if (!is_string($format)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringMatches($format) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string matches a given format file. + * + * @param string $formatFile + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertStringMatchesFormatFile($formatFile, $string, $message = '') + { + self::assertFileExists($formatFile, $message); + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringMatches( + file_get_contents($formatFile) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string does not match a given format string. + * + * @param string $formatFile + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ + public static function assertStringNotMatchesFormatFile($formatFile, $string, $message = '') + { + self::assertFileExists($formatFile, $message); + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringMatches( + file_get_contents($formatFile) + ) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string starts with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ + public static function assertStringStartsWith($prefix, $string, $message = '') + { + if (!is_string($prefix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringStartsWith( + $prefix + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ + public static function assertStringStartsNotWith($prefix, $string, $message = '') + { + if (!is_string($prefix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringStartsWith($prefix) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string ends with a given prefix. + * + * @param string $suffix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ + public static function assertStringEndsWith($suffix, $string, $message = '') + { + if (!is_string($suffix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringEndsWith($suffix); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string ends not with a given prefix. + * + * @param string $suffix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ + public static function assertStringEndsNotWith($suffix, $string, $message = '') + { + if (!is_string($suffix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringEndsWith($suffix) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that two XML files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '') + { + self::assertFileExists($expectedFile); + self::assertFileExists($actualFile); + + $expected = new DOMDocument; + $expected->preserveWhiteSpace = FALSE; + $expected->load($expectedFile); + + $actual = new DOMDocument; + $actual->preserveWhiteSpace = FALSE; + $actual->load($actualFile); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '') + { + self::assertFileExists($expectedFile); + self::assertFileExists($actualFile); + + $expected = new DOMDocument; + $expected->preserveWhiteSpace = FALSE; + $expected->load($expectedFile); + + $actual = new DOMDocument; + $actual->preserveWhiteSpace = FALSE; + $actual->load($actualFile); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.3.0 + */ + public static function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '') + { + self::assertFileExists($expectedFile); + + $expected = new DOMDocument; + $expected->preserveWhiteSpace = FALSE; + $expected->load($expectedFile); + + $actual = new DOMDocument; + $actual->preserveWhiteSpace = FALSE; + $actual->loadXML($actualXml); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are not equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.3.0 + */ + public static function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '') + { + self::assertFileExists($expectedFile); + + $expected = new DOMDocument; + $expected->preserveWhiteSpace = FALSE; + $expected->load($expectedFile); + + $actual = new DOMDocument; + $actual->preserveWhiteSpace = FALSE; + $actual->loadXML($actualXml); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '') + { + $expected = new DOMDocument; + $expected->preserveWhiteSpace = FALSE; + $expected->loadXML($expectedXml); + + $actual = new DOMDocument; + $actual->preserveWhiteSpace = FALSE; + $actual->loadXML($actualXml); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are not equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.1.0 + */ + public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '') + { + $expected = new DOMDocument; + $expected->preserveWhiteSpace = FALSE; + $expected->loadXML($expectedXml); + + $actual = new DOMDocument; + $actual->preserveWhiteSpace = FALSE; + $actual->loadXML($actualXml); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that a hierarchy of DOMElements matches. + * + * @param DOMElement $expectedElement + * @param DOMElement $actualElement + * @param boolean $checkAttributes + * @param string $message + * @author Mattis Stordalen Flister + * @since Method available since Release 3.3.0 + */ + public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = FALSE, $message = '') + { + self::assertEquals( + $expectedElement->tagName, + $actualElement->tagName, + $message + ); + + if ($checkAttributes) { + self::assertEquals( + $expectedElement->attributes->length, + $actualElement->attributes->length, + sprintf( + '%s%sNumber of attributes on node "%s" does not match', + $message, + !empty($message) ? "\n" : '', + $expectedElement->tagName + ) + ); + + for ($i = 0 ; $i < $expectedElement->attributes->length; $i++) { + $expectedAttribute = $expectedElement->attributes->item($i); + $actualAttribute = $actualElement->attributes->getNamedItem( + $expectedAttribute->name + ); + + if (!$actualAttribute) { + self::fail( + sprintf( + '%s%sCould not find attribute "%s" on node "%s"', + $message, + !empty($message) ? "\n" : '', + $expectedAttribute->name, + $expectedElement->tagName + ) + ); + } + } + } + + PHPUnit_Util_XML::removeCharacterDataNodes($expectedElement); + PHPUnit_Util_XML::removeCharacterDataNodes($actualElement); + + self::assertEquals( + $expectedElement->childNodes->length, + $actualElement->childNodes->length, + sprintf( + '%s%sNumber of child nodes of "%s" differs', + $message, + !empty($message) ? "\n" : '', + $expectedElement->tagName + ) + ); + + for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { + self::assertEqualXMLStructure( + $expectedElement->childNodes->item($i), + $actualElement->childNodes->item($i), + $checkAttributes, + $message + ); + } + } + + /** + * Assert the presence, absence, or count of elements in a document matching + * the CSS $selector, regardless of the contents of those elements. + * + * The first argument, $selector, is the CSS selector used to match + * the elements in the $actual document. + * + * The second argument, $count, can be either boolean or numeric. + * When boolean, it asserts for presence of elements matching the selector + * (TRUE) or absence of elements (FALSE). + * When numeric, it asserts the count of elements. + * + * assertSelectCount("#binder", true, $xml); // any? + * assertSelectCount(".binder", 3, $xml); // exactly 3? + * + * @param array $selector + * @param integer $count + * @param mixed $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function assertSelectCount($selector, $count, $actual, $message = '', $isHtml = TRUE) + { + self::assertSelectEquals( + $selector, TRUE, $count, $actual, $message, $isHtml + ); + } + + /** + * assertSelectRegExp("#binder .name", "/Mike|Derek/", true, $xml); // any? + * assertSelectRegExp("#binder .name", "/Mike|Derek/", 3, $xml); // 3? + * + * @param array $selector + * @param string $pattern + * @param integer $count + * @param mixed $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function assertSelectRegExp($selector, $pattern, $count, $actual, $message = '', $isHtml = TRUE) + { + self::assertSelectEquals( + $selector, "regexp:$pattern", $count, $actual, $message, $isHtml + ); + } + + /** + * assertSelectEquals("#binder .name", "Chuck", true, $xml); // any? + * assertSelectEquals("#binder .name", "Chuck", false, $xml); // none? + * + * @param array $selector + * @param string $content + * @param integer $count + * @param mixed $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function assertSelectEquals($selector, $content, $count, $actual, $message = '', $isHtml = TRUE) + { + $tags = PHPUnit_Util_XML::cssSelect( + $selector, $content, $actual, $isHtml + ); + + // assert specific number of elements + if (is_numeric($count)) { + $counted = $tags ? count($tags) : 0; + self::assertEquals($count, $counted, $message); + } + + // assert any elements exist if true, assert no elements exist if false + else if (is_bool($count)) { + $any = count($tags) > 0 && $tags[0] instanceof DOMNode; + + if ($count) { + self::assertTrue($any, $message); + } else { + self::assertFalse($any, $message); + } + } + + // check for range number of elements + else if (is_array($count) && + (isset($count['>']) || isset($count['<']) || + isset($count['>=']) || isset($count['<=']))) { + $counted = $tags ? count($tags) : 0; + + if (isset($count['>'])) { + self::assertTrue($counted > $count['>'], $message); + } + + if (isset($count['>='])) { + self::assertTrue($counted >= $count['>='], $message); + } + + if (isset($count['<'])) { + self::assertTrue($counted < $count['<'], $message); + } + + if (isset($count['<='])) { + self::assertTrue($counted <= $count['<='], $message); + } + } else { + throw new PHPUnit_Framework_Exception; + } + } + + /** + * Evaluate an HTML or XML string and assert its structure and/or contents. + * + * The first argument ($matcher) is an associative array that specifies the + * match criteria for the assertion: + * + * - `id` : the node with the given id attribute must match the + * corresponsing value. + * - `tag` : the node type must match the corresponding value. + * - `attributes` : a hash. The node's attributres must match the + * corresponsing values in the hash. + * - `content` : The text content must match the given value. + * - `parent` : a hash. The node's parent must match the + * corresponsing hash. + * - `child` : a hash. At least one of the node's immediate children + * must meet the criteria described by the hash. + * - `ancestor` : a hash. At least one of the node's ancestors must + * meet the criteria described by the hash. + * - `descendant` : a hash. At least one of the node's descendants must + * meet the criteria described by the hash. + * - `children` : a hash, for counting children of a node. + * Accepts the keys: + * - `count` : a number which must equal the number of children + * that match + * - `less_than` : the number of matching children must be greater + * than this number + * - `greater_than` : the number of matching children must be less than + * this number + * - `only` : another hash consisting of the keys to use to match + * on the children, and only matching children will be + * counted + * + * + * // Matcher that asserts that there is an element with an id="my_id". + * $matcher = array('id' => 'my_id'); + * + * // Matcher that asserts that there is a "span" tag. + * $matcher = array('tag' => 'span'); + * + * // Matcher that asserts that there is a "span" tag with the content + * // "Hello World". + * $matcher = array('tag' => 'span', 'content' => 'Hello World'); + * + * // Matcher that asserts that there is a "span" tag with content matching + * // the regular expression pattern. + * $matcher = array('tag' => 'span', 'content' => 'regexp:/Try P(HP|ython)/'); + * + * // Matcher that asserts that there is a "span" with an "list" class + * // attribute. + * $matcher = array( + * 'tag' => 'span', + * 'attributes' => array('class' => 'list') + * ); + * + * // Matcher that asserts that there is a "span" inside of a "div". + * $matcher = array( + * 'tag' => 'span', + * 'parent' => array('tag' => 'div') + * ); + * + * // Matcher that asserts that there is a "span" somewhere inside a + * // "table". + * $matcher = array( + * 'tag' => 'span', + * 'ancestor' => array('tag' => 'table') + * ); + * + * // Matcher that asserts that there is a "span" with at least one "em" + * // child. + * $matcher = array( + * 'tag' => 'span', + * 'child' => array('tag' => 'em') + * ); + * + * // Matcher that asserts that there is a "span" containing a (possibly + * // nested) "strong" tag. + * $matcher = array( + * 'tag' => 'span', + * 'descendant' => array('tag' => 'strong') + * ); + * + * // Matcher that asserts that there is a "span" containing 5-10 "em" tags + * // as immediate children. + * $matcher = array( + * 'tag' => 'span', + * 'children' => array( + * 'less_than' => 11, + * 'greater_than' => 4, + * 'only' => array('tag' => 'em') + * ) + * ); + * + * // Matcher that asserts that there is a "div", with an "ul" ancestor and + * // a "li" parent (with class="enum"), and containing a "span" descendant + * // that contains an element with id="my_test" and the text "Hello World". + * $matcher = array( + * 'tag' => 'div', + * 'ancestor' => array('tag' => 'ul'), + * 'parent' => array( + * 'tag' => 'li', + * 'attributes' => array('class' => 'enum') + * ), + * 'descendant' => array( + * 'tag' => 'span', + * 'child' => array( + * 'id' => 'my_test', + * 'content' => 'Hello World' + * ) + * ) + * ); + * + * // Use assertTag() to apply a $matcher to a piece of $html. + * $this->assertTag($matcher, $html); + * + * // Use assertTag() to apply a $matcher to a piece of $xml. + * $this->assertTag($matcher, $xml, '', FALSE); + * + * + * The second argument ($actual) is a string containing either HTML or + * XML text to be tested. + * + * The third argument ($message) is an optional message that will be + * used if the assertion fails. + * + * The fourth argument ($html) is an optional flag specifying whether + * to load the $actual string into a DOMDocument using the HTML or + * XML load strategy. It is TRUE by default, which assumes the HTML + * load strategy. In many cases, this will be acceptable for XML as well. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function assertTag($matcher, $actual, $message = '', $isHtml = TRUE) + { + $dom = PHPUnit_Util_XML::load($actual, $isHtml); + $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $isHtml); + $matched = count($tags) > 0 && $tags[0] instanceof DOMNode; + + self::assertTrue($matched, $message); + } + + /** + * This assertion is the exact opposite of assertTag(). + * + * Rather than asserting that $matcher results in a match, it asserts that + * $matcher does not match. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function assertNotTag($matcher, $actual, $message = '', $isHtml = TRUE) + { + $dom = PHPUnit_Util_XML::load($actual, $isHtml); + $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $isHtml); + $matched = count($tags) > 0 && $tags[0] instanceof DOMNode; + + self::assertFalse($matched, $message); + } + + /** + * Evaluates a PHPUnit_Framework_Constraint matcher object. + * + * @param mixed $value + * @param PHPUnit_Framework_Constraint $constraint + * @param string $message + * @since Method available since Release 3.0.0 + */ + public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '') + { + self::$count += count($constraint); + + $constraint->evaluate($value, $message); + } + + /** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '') + { + $expected = json_decode($expectedJson); + if ($jsonError = json_last_error()) { + $message .= + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + $jsonError, + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::translateTypeToPrefix('expected') + ); + } + + $actual = json_decode($actualJson); + if ($jsonError = json_last_error()) { + $message .= + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + $jsonError, + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::translateTypeToPrefix('actual') + ); + } + return self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '') + { + $expected = json_decode($expectedJson); + if ($jsonError = json_last_error()) { + $message .= + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + $jsonError, + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::translateTypeToPrefix('expected') + ); + } + + $actual = json_decode($actualJson); + if ($jsonError = json_last_error()) { + $message .= + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + $jsonError, + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::translateTypeToPrefix('actual') + ); + } + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '') + { + self::assertFileExists($expectedFile, $message); + + if (!is_string($actualJson)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + // call constraint + $constraint = new PHPUnit_Framework_Constraint_JsonMatches( + file_get_contents($expectedFile) + ); + + self::assertThat($actualJson, $constraint, $message); + } + + /** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '') + { + self::assertFileExists($expectedFile, $message); + + if (!is_string($actualJson)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + // call constraint + $constraint = new PHPUnit_Framework_Constraint_JsonMatches( + file_get_contents($expectedFile) + ); + + self::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraint), $message); + } + + /** + * Asserts that two JSON files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + */ + public static function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '') + { + self::assertFileExists($expectedFile, $message); + self::assertFileExists($actualFile, $message); + + $actualJson = file_get_contents($actualFile); + $expectedJson = file_get_contents($expectedFile); + + // call constraint + $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches( + file_get_contents($expectedFile) + ); + + $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson); + + self::assertThat($expectedJson, new PHPUnit_Framework_Constraint_Not($constraintActual), $message); + self::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraintExpected), $message); + } + + /** + * Asserts that two JSON files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + */ + public static function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '') + { + self::assertFileExists($expectedFile, $message); + self::assertFileExists($actualFile, $message); + + $actualJson = file_get_contents($actualFile); + $expectedJson = file_get_contents($expectedFile); + + // call constraint + $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches( + file_get_contents($expectedFile) + ); + + $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson); + + self::assertThat($expectedJson, $constraintActual, $message); + self::assertThat($actualJson, $constraintExpected, $message); + } + + /** + * Returns a PHPUnit_Framework_Constraint_And matcher object. + * + * @return PHPUnit_Framework_Constraint_And + * @since Method available since Release 3.0.0 + */ + public static function logicalAnd() + { + $constraints = func_get_args(); + + $constraint = new PHPUnit_Framework_Constraint_And; + $constraint->setConstraints($constraints); + + return $constraint; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Or matcher object. + * + * @return PHPUnit_Framework_Constraint_Or + * @since Method available since Release 3.0.0 + */ + public static function logicalOr() + { + $constraints = func_get_args(); + + $constraint = new PHPUnit_Framework_Constraint_Or; + $constraint->setConstraints($constraints); + + return $constraint; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Not matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * @return PHPUnit_Framework_Constraint_Not + * @since Method available since Release 3.0.0 + */ + public static function logicalNot(PHPUnit_Framework_Constraint $constraint) + { + return new PHPUnit_Framework_Constraint_Not($constraint); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Xor matcher object. + * + * @return PHPUnit_Framework_Constraint_Xor + * @since Method available since Release 3.0.0 + */ + public static function logicalXor() + { + $constraints = func_get_args(); + + $constraint = new PHPUnit_Framework_Constraint_Xor; + $constraint->setConstraints($constraints); + + return $constraint; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object. + * + * @return PHPUnit_Framework_Constraint_IsAnything + * @since Method available since Release 3.0.0 + */ + public static function anything() + { + return new PHPUnit_Framework_Constraint_IsAnything; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object. + * + * @return PHPUnit_Framework_Constraint_IsTrue + * @since Method available since Release 3.3.0 + */ + public static function isTrue() + { + return new PHPUnit_Framework_Constraint_IsTrue; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Callback matcher object. + * + * @return PHPUnit_Framework_Constraint_Callback + */ + public static function callback($callback) + { + return new PHPUnit_Framework_Constraint_Callback($callback); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object. + * + * @return PHPUnit_Framework_Constraint_IsFalse + * @since Method available since Release 3.3.0 + */ + public static function isFalse() + { + return new PHPUnit_Framework_Constraint_IsFalse; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsNull matcher object. + * + * @return PHPUnit_Framework_Constraint_IsNull + * @since Method available since Release 3.3.0 + */ + public static function isNull() + { + return new PHPUnit_Framework_Constraint_IsNull; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Attribute matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_Attribute + * @since Method available since Release 3.1.0 + */ + public static function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName) + { + return new PHPUnit_Framework_Constraint_Attribute( + $constraint, $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher + * object. + * + * @param mixed $value + * @param boolean $checkForObjectIdentity + * @return PHPUnit_Framework_Constraint_TraversableContains + * @since Method available since Release 3.0.0 + */ + public static function contains($value, $checkForObjectIdentity = TRUE) + { + return new PHPUnit_Framework_Constraint_TraversableContains($value, $checkForObjectIdentity); + } + + /** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $type + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + * @since Method available since Release 3.1.4 + */ + public static function containsOnly($type) + { + return new PHPUnit_Framework_Constraint_TraversableContainsOnly($type); + } + + /** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $classname + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + */ + public static function containsOnlyInstancesOf($classname) + { + return new PHPUnit_Framework_Constraint_TraversableContainsOnly($classname, FALSE); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object. + * + * @param mixed $key + * @return PHPUnit_Framework_Constraint_ArrayHasKey + * @since Method available since Release 3.0.0 + */ + public static function arrayHasKey($key) + { + return new PHPUnit_Framework_Constraint_ArrayHasKey($key); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object. + * + * @param mixed $value + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @return PHPUnit_Framework_Constraint_IsEqual + * @since Method available since Release 3.0.0 + */ + public static function equalTo($value, $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + return new PHPUnit_Framework_Constraint_IsEqual( + $value, $delta, $maxDepth, $canonicalize, $ignoreCase + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object + * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher + * object. + * + * @param string $attributeName + * @param mixed $value + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @return PHPUnit_Framework_Constraint_Attribute + * @since Method available since Release 3.1.0 + */ + public static function attributeEqualTo($attributeName, $value, $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + return self::attribute( + self::equalTo( + $value, $delta, $maxDepth, $canonicalize, $ignoreCase + ), + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object. + * + * @return PHPUnit_Framework_Constraint_IsEmpty + * @since Method available since Release 3.5.0 + */ + public static function isEmpty() + { + return new PHPUnit_Framework_Constraint_IsEmpty; + } + /** + * Returns a PHPUnit_Framework_Constraint_FileExists matcher object. + * + * @return PHPUnit_Framework_Constraint_FileExists + * @since Method available since Release 3.0.0 + */ + public static function fileExists() + { + return new PHPUnit_Framework_Constraint_FileExists; + } + + /** + * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_GreaterThan + * @since Method available since Release 3.0.0 + */ + public static function greaterThan($value) + { + return new PHPUnit_Framework_Constraint_GreaterThan($value); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_Or + * @since Method available since Release 3.1.0 + */ + public static function greaterThanOrEqual($value) + { + return self::logicalOr( + new PHPUnit_Framework_Constraint_IsEqual($value), + new PHPUnit_Framework_Constraint_GreaterThan($value) + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object. + * + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_ClassHasAttribute + * @since Method available since Release 3.1.0 + */ + public static function classHasAttribute($attributeName) + { + return new PHPUnit_Framework_Constraint_ClassHasAttribute( + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher + * object. + * + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * @since Method available since Release 3.1.0 + */ + public static function classHasStaticAttribute($attributeName) + { + return new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object. + * + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_ObjectHasAttribute + * @since Method available since Release 3.0.0 + */ + public static function objectHasAttribute($attributeName) + { + return new PHPUnit_Framework_Constraint_ObjectHasAttribute( + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_IsIdentical + * @since Method available since Release 3.0.0 + */ + public static function identicalTo($value) + { + return new PHPUnit_Framework_Constraint_IsIdentical($value); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object. + * + * @param string $className + * @return PHPUnit_Framework_Constraint_IsInstanceOf + * @since Method available since Release 3.0.0 + */ + public static function isInstanceOf($className) + { + return new PHPUnit_Framework_Constraint_IsInstanceOf($className); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsType matcher object. + * + * @param string $type + * @return PHPUnit_Framework_Constraint_IsType + * @since Method available since Release 3.0.0 + */ + public static function isType($type) + { + return new PHPUnit_Framework_Constraint_IsType($type); + } + + /** + * Returns a PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_LessThan + * @since Method available since Release 3.0.0 + */ + public static function lessThan($value) + { + return new PHPUnit_Framework_Constraint_LessThan($value); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_Or + * @since Method available since Release 3.1.0 + */ + public static function lessThanOrEqual($value) + { + return self::logicalOr( + new PHPUnit_Framework_Constraint_IsEqual($value), + new PHPUnit_Framework_Constraint_LessThan($value) + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object. + * + * @param string $pattern + * @return PHPUnit_Framework_Constraint_PCREMatch + * @since Method available since Release 3.0.0 + */ + public static function matchesRegularExpression($pattern) + { + return new PHPUnit_Framework_Constraint_PCREMatch($pattern); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object. + * + * @param string $string + * @return PHPUnit_Framework_Constraint_StringMatches + * @since Method available since Release 3.5.0 + */ + public static function matches($string) + { + return new PHPUnit_Framework_Constraint_StringMatches($string); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object. + * + * @param mixed $prefix + * @return PHPUnit_Framework_Constraint_StringStartsWith + * @since Method available since Release 3.4.0 + */ + public static function stringStartsWith($prefix) + { + return new PHPUnit_Framework_Constraint_StringStartsWith($prefix); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringContains matcher object. + * + * @param string $string + * @param boolean $case + * @return PHPUnit_Framework_Constraint_StringContains + * @since Method available since Release 3.0.0 + */ + public static function stringContains($string, $case = TRUE) + { + return new PHPUnit_Framework_Constraint_StringContains($string, $case); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object. + * + * @param mixed $suffix + * @return PHPUnit_Framework_Constraint_StringEndsWith + * @since Method available since Release 3.4.0 + */ + public static function stringEndsWith($suffix) + { + return new PHPUnit_Framework_Constraint_StringEndsWith($suffix); + } + + /** + * Fails a test with the given message. + * + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function fail($message = '') + { + throw new PHPUnit_Framework_AssertionFailedError($message); + } + + /** + * Returns the value of an attribute of a class or an object. + * This also works for attributes that are declared protected or private. + * + * @param mixed $classOrObject + * @param string $attributeName + * @return mixed + * @throws PHPUnit_Framework_Exception + */ + public static function readAttribute($classOrObject, $attributeName) + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + if (is_string($classOrObject)) { + if (!class_exists($classOrObject)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'class name' + ); + } + + return PHPUnit_Util_Class::getStaticAttribute( + $classOrObject, + $attributeName + ); + } + + else if (is_object($classOrObject)) { + return PHPUnit_Util_Class::getObjectAttribute( + $classOrObject, + $attributeName + ); + } + + else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'class name or object' + ); + } + } + + /** + * Mark the test as incomplete. + * + * @param string $message + * @throws PHPUnit_Framework_IncompleteTestError + * @since Method available since Release 3.0.0 + */ + public static function markTestIncomplete($message = '') + { + throw new PHPUnit_Framework_IncompleteTestError($message); + } + + /** + * Mark the test as skipped. + * + * @param string $message + * @throws PHPUnit_Framework_SkippedTestError + * @since Method available since Release 3.0.0 + */ + public static function markTestSkipped($message = '') + { + throw new PHPUnit_Framework_SkippedTestError($message); + } + + /** + * Return the current assertion count. + * + * @return integer + * @since Method available since Release 3.3.3 + */ + public static function getCount() + { + return self::$count; + } + + /** + * Reset the assertion counter. + * + * @since Method available since Release 3.3.3 + */ + public static function resetCount() + { + self::$count = 0; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php new file mode 100644 index 0000000..057a950 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php @@ -0,0 +1,1972 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */ + +/** + * Returns a matcher that matches when the method it is evaluated for + * is executed zero or more times. + * + * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount + * @since Method available since Release 3.0.0 + */ +function any() +{ + return PHPUnit_Framework_TestCase::any(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object. + * + * @return PHPUnit_Framework_Constraint_IsAnything + * @since Method available since Release 3.0.0 + */ +function anything() +{ + return PHPUnit_Framework_Assert::anything(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object. + * + * @param mixed $key + * @return PHPUnit_Framework_Constraint_ArrayHasKey + * @since Method available since Release 3.0.0 + */ +function arrayHasKey($key) +{ + return PHPUnit_Framework_Assert::arrayHasKey($key); +} + +/** + * Asserts that an array has a specified key. + * + * @param mixed $key + * @param array $array + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertArrayHasKey($key, array $array, $message = '') +{ + return PHPUnit_Framework_Assert::assertArrayHasKey($key, $array, $message); +} + +/** + * Asserts that an array does not have a specified key. + * + * @param mixed $key + * @param array $array + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertArrayNotHasKey($key, array $array, $message = '') +{ + return PHPUnit_Framework_Assert::assertArrayNotHasKey($key, $array, $message); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 3.0.0 + */ +function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) +{ + return PHPUnit_Framework_Assert::assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message, $ignoreCase, $checkForObjectIdentity); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains only values of a given type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ +function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = NULL, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType, $message); +} + +/** + * Asserts the number of elements of an array, Countable or Iterator + * that is stored in an attribute. + * + * @param integer $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.6.0 + */ +function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message); +} + +/** + * Asserts that a static attribute of a class or an attribute of an object + * is empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message); +} + +/** + * Asserts that a variable is equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ +function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); +} + +/** + * Asserts that an attribute is greater than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message); +} + +/** + * Asserts that an attribute is greater than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeInternalType($expected, $attributeName, $classOrObject, $message); +} + +/** + * Asserts that an attribute is smaller than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message); +} + +/** + * Asserts that an attribute is smaller than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 3.0.0 + */ +function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) +{ + return PHPUnit_Framework_Assert::assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message, $ignoreCase, $checkForObjectIdentity); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain only values of a given + * type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ +function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = NULL, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType, $message); +} + +/** + * Asserts the number of elements of an array, Countable or Iterator + * that is stored in an attribute. + * + * @param integer $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.6.0 + */ +function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message); +} + +/** + * Asserts that a static attribute of a class or an attribute of an object + * is not empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message); +} + +/** + * Asserts that a variable is not equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ +function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message); +} + +/** + * Asserts that a variable and an attribute of an object do not have the + * same type and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ +function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message); +} + +/** + * Asserts that a variable and an attribute of an object have the same type + * and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ +function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return PHPUnit_Framework_Assert::assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message); +} + +/** + * Asserts that a class has a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertClassHasAttribute($attributeName, $className, $message = '') +{ + return PHPUnit_Framework_Assert::assertClassHasAttribute($attributeName, $className, $message); +} + +/** + * Asserts that a class has a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertClassHasStaticAttribute($attributeName, $className, $message = '') +{ + return PHPUnit_Framework_Assert::assertClassHasStaticAttribute($attributeName, $className, $message); +} + +/** + * Asserts that a class does not have a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertClassNotHasAttribute($attributeName, $className, $message = '') +{ + return PHPUnit_Framework_Assert::assertClassNotHasAttribute($attributeName, $className, $message); +} + +/** + * Asserts that a class does not have a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertClassNotHasStaticAttribute($attributeName, $className, $message = '') +{ + return PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute($attributeName, $className, $message); +} + +/** + * Asserts that a haystack contains a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 2.1.0 + */ +function assertContains($needle, $haystack, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) +{ + return PHPUnit_Framework_Assert::assertContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity); +} + +/** + * Asserts that a haystack contains only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ +function assertContainsOnly($type, $haystack, $isNativeType = NULL, $message = '') +{ + return PHPUnit_Framework_Assert::assertContainsOnly($type, $haystack, $isNativeType, $message); +} + +/** + * Asserts the number of elements of an array, Countable or Iterator. + * + * @param integer $expectedCount + * @param mixed $haystack + * @param string $message + */ +function assertCount($expectedCount, $haystack, $message = '') +{ + return PHPUnit_Framework_Assert::assertCount($expectedCount, $haystack, $message); +} + +/** + * Asserts that a variable is empty. + * + * @param mixed $actual + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertEmpty($actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertEmpty($actual, $message); +} + +/** + * Asserts that a hierarchy of DOMElements matches. + * + * @param DOMElement $expectedElement + * @param DOMElement $actualElement + * @param boolean $checkAttributes + * @param string $message + * @author Mattis Stordalen Flister + * @since Method available since Release 3.3.0 + */ +function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = FALSE, $message = '') +{ + return PHPUnit_Framework_Assert::assertEqualXMLStructure($expectedElement, $actualElement, $checkAttributes, $message); +} + +/** + * Asserts that two variables are equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ +function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); +} + +/** + * Asserts that a condition is false. + * + * @param boolean $condition + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertFalse($condition, $message = '') +{ + return PHPUnit_Framework_Assert::assertFalse($condition, $message); +} + +/** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.2.14 + */ +function assertFileEquals($expected, $actual, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertFileEquals($expected, $actual, $message, $canonicalize, $ignoreCase); +} + +/** + * Asserts that a file exists. + * + * @param string $filename + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertFileExists($filename, $message = '') +{ + return PHPUnit_Framework_Assert::assertFileExists($filename, $message); +} + +/** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.2.14 + */ +function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertFileNotEquals($expected, $actual, $message, $canonicalize, $ignoreCase); +} + +/** + * Asserts that a file does not exist. + * + * @param string $filename + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertFileNotExists($filename, $message = '') +{ + return PHPUnit_Framework_Assert::assertFileNotExists($filename, $message); +} + +/** + * Asserts that a value is greater than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertGreaterThan($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertGreaterThan($expected, $actual, $message); +} + +/** + * Asserts that a value is greater than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertGreaterThanOrEqual($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertGreaterThanOrEqual($expected, $actual, $message); +} + +/** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertInstanceOf($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertInstanceOf($expected, $actual, $message); +} + +/** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertInternalType($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertInternalType($expected, $actual, $message); +} + +/** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + * @since Method available since Release 3.7.0 + */ +function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '') +{ + return PHPUnit_Framework_Assert::assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = ''); +} + +/** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + * @since Method available since Release 3.7.0 + */ +function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '') +{ + return PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = ''); +} + +/** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + * @since Method available since Release 3.7.0 + */ +function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '') +{ + return PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = ''); +} + +/** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + * @since Method available since Release 3.7.0 + */ +function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '') +{ + return PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = ''); +} + +/** + * Asserts that two JSON files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * @since Method available since Release 3.7.0 + */ +function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '') +{ + return PHPUnit_Framework_Assert::assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = ''); +} + +/** + * Asserts that two JSON files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * @since Method available since Release 3.7.0 + */ +function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '') +{ + return PHPUnit_Framework_Assert::assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = ''); +} + +/** + * Asserts that a value is smaller than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertLessThan($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertLessThan($expected, $actual, $message); +} + +/** + * Asserts that a value is smaller than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertLessThanOrEqual($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertLessThanOrEqual($expected, $actual, $message); +} + +/** + * Asserts that a haystack does not contain a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param boolean $ignoreCase + * @param boolean $checkForObjectIdentity + * @since Method available since Release 2.1.0 + */ +function assertNotContains($needle, $haystack, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) +{ + return PHPUnit_Framework_Assert::assertNotContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity); +} + +/** + * Asserts that a haystack does not contain only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param boolean $isNativeType + * @param string $message + * @since Method available since Release 3.1.4 + */ +function assertNotContainsOnly($type, $haystack, $isNativeType = NULL, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotContainsOnly($type, $haystack, $isNativeType, $message); +} + +/** + * Asserts the number of elements of an array, Countable or Iterator. + * + * @param integer $expectedCount + * @param mixed $haystack + * @param string $message + */ +function assertNotCount($expectedCount, $haystack, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotCount($expectedCount, $haystack, $message); +} + +/** + * Asserts that a variable is not empty. + * + * @param mixed $actual + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertNotEmpty($actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotEmpty($actual, $message); +} + +/** + * Asserts that two variables are not equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 2.3.0 + */ +function assertNotEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertNotEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); +} + +/** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertNotInstanceOf($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotInstanceOf($expected, $actual, $message); +} + +/** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertNotInternalType($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotInternalType($expected, $actual, $message); +} + +/** + * Asserts that a variable is not NULL. + * + * @param mixed $actual + * @param string $message + */ +function assertNotNull($actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotNull($actual, $message); +} + +/** + * Asserts that a string does not match a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + * @since Method available since Release 2.1.0 + */ +function assertNotRegExp($pattern, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotRegExp($pattern, $string, $message); +} + +/** + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ +function assertNotSame($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotSame($expected, $actual, $message); +} + +/** + * Assert that the size of two arrays (or `Countable` or `Iterator` objects) + * is not the same. + * + * @param integer $expected + * @param mixed $actual + * @param string $message + */ +function assertNotSameSize($expectedCount, $haystack, $message = '') +{ + return PHPUnit_Framework_Assert::assertNotSameSize($expectedCount, $haystack, $message); +} + +/** + * This assertion is the exact opposite of assertTag(). + * + * Rather than asserting that $matcher results in a match, it asserts that + * $matcher does not match. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ +function assertNotTag($matcher, $actual, $message = '', $isHtml = TRUE) +{ + return PHPUnit_Framework_Assert::assertNotTag($matcher, $actual, $message, $isHtml); +} + +/** + * Asserts that a variable is NULL. + * + * @param mixed $actual + * @param string $message + */ +function assertNull($actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertNull($actual, $message); +} + +/** + * Asserts that an object has a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertObjectHasAttribute($attributeName, $object, $message = '') +{ + return PHPUnit_Framework_Assert::assertObjectHasAttribute($attributeName, $object, $message); +} + +/** + * Asserts that an object does not have a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertObjectNotHasAttribute($attributeName, $object, $message = '') +{ + return PHPUnit_Framework_Assert::assertObjectNotHasAttribute($attributeName, $object, $message); +} + +/** + * Asserts that a string matches a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + */ +function assertRegExp($pattern, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertRegExp($pattern, $string, $message); +} + +/** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ +function assertSame($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertSame($expected, $actual, $message); +} + +/** + * Assert that the size of two arrays (or `Countable` or `Iterator` objects) + * is the same. + * + * @param integer $expected + * @param mixed $actual + * @param string $message + */ +function assertSameSize($expected, $actual, $message = '') +{ + return PHPUnit_Framework_Assert::assertSameSize($expected, $actual, $message); +} + +/** + * Assert the presence, absence, or count of elements in a document matching + * the CSS $selector, regardless of the contents of those elements. + * + * The first argument, $selector, is the CSS selector used to match + * the elements in the $actual document. + * + * The second argument, $count, can be either boolean or numeric. + * When boolean, it asserts for presence of elements matching the selector + * (TRUE) or absence of elements (FALSE). + * When numeric, it asserts the count of elements. + * + * assertSelectCount("#binder", true, $xml); // any? + * assertSelectCount(".binder", 3, $xml); // exactly 3? + * + * @param array $selector + * @param integer $count + * @param mixed $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ +function assertSelectCount($selector, $count, $actual, $message = '', $isHtml = TRUE) +{ + return PHPUnit_Framework_Assert::assertSelectCount($selector, $count, $actual, $message, $isHtml); +} + +/** + * assertSelectEquals("#binder .name", "Chuck", true, $xml); // any? + * assertSelectEquals("#binder .name", "Chuck", false, $xml); // none? + * + * @param array $selector + * @param string $content + * @param integer $count + * @param mixed $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ +function assertSelectEquals($selector, $content, $count, $actual, $message = '', $isHtml = TRUE) +{ + return PHPUnit_Framework_Assert::assertSelectEquals($selector, $content, $count, $actual, $message, $isHtml); +} + +/** + * assertSelectRegExp("#binder .name", "/Mike|Derek/", true, $xml); // any? + * assertSelectRegExp("#binder .name", "/Mike|Derek/", 3, $xml);// 3? + * + * @param array $selector + * @param string $pattern + * @param integer $count + * @param mixed $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ +function assertSelectRegExp($selector, $pattern, $count, $actual, $message = '', $isHtml = TRUE) +{ + return PHPUnit_Framework_Assert::assertSelectRegExp($selector, $pattern, $count, $actual, $message, $isHtml); +} + +/** + * Asserts that a string ends not with a given prefix. + * + * @param string $suffix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ +function assertStringEndsNotWith($suffix, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringEndsNotWith($suffix, $string, $message); +} + +/** + * Asserts that a string ends with a given prefix. + * + * @param string $suffix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ +function assertStringEndsWith($suffix, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringEndsWith($suffix, $string, $message); +} + +/** + * Asserts that the contents of a string is equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.3.0 + */ +function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertStringEqualsFile($expectedFile, $actualString, $message, $canonicalize, $ignoreCase); +} + +/** + * Asserts that a string matches a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertStringMatchesFormat($format, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringMatchesFormat($format, $string, $message); +} + +/** + * Asserts that a string matches a given format file. + * + * @param string $formatFile + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertStringMatchesFormatFile($formatFile, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringMatchesFormatFile($formatFile, $string, $message); +} + +/** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @since Method available since Release 3.3.0 + */ +function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::assertStringNotEqualsFile($expectedFile, $actualString, $message, $canonicalize, $ignoreCase); +} + +/** + * Asserts that a string does not match a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertStringNotMatchesFormat($format, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringNotMatchesFormat($format, $string, $message); +} + +/** + * Asserts that a string does not match a given format string. + * + * @param string $formatFile + * @param string $string + * @param string $message + * @since Method available since Release 3.5.0 + */ +function assertStringNotMatchesFormatFile($formatFile, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringNotMatchesFormatFile($formatFile, $string, $message); +} + +/** + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ +function assertStringStartsNotWith($prefix, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringStartsNotWith($prefix, $string, $message); +} + +/** + * Asserts that a string starts with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * @since Method available since Release 3.4.0 + */ +function assertStringStartsWith($prefix, $string, $message = '') +{ + return PHPUnit_Framework_Assert::assertStringStartsWith($prefix, $string, $message); +} + +/** + * Evaluate an HTML or XML string and assert its structure and/or contents. + * + * The first argument ($matcher) is an associative array that specifies the + * match criteria for the assertion: + * + * - `id` : the node with the given id attribute must match the + * corresponsing value. + * - `tag` : the node type must match the corresponding value. + * - `attributes` : a hash. The node's attributres must match the + * corresponsing values in the hash. + * - `content` : The text content must match the given value. + * - `parent` : a hash. The node's parent must match the + * corresponsing hash. + * - `child`: a hash. At least one of the node's immediate children + * must meet the criteria described by the hash. + * - `ancestor` : a hash. At least one of the node's ancestors must + * meet the criteria described by the hash. + * - `descendant` : a hash. At least one of the node's descendants must + * meet the criteria described by the hash. + * - `children` : a hash, for counting children of a node. + * Accepts the keys: + *- `count`: a number which must equal the number of children + * that match + *- `less_than`: the number of matching children must be greater + * than this number + *- `greater_than` : the number of matching children must be less than + * this number + *- `only` : another hash consisting of the keys to use to match + * on the children, and only matching children will be + * counted + * + * + * // Matcher that asserts that there is an element with an id="my_id". + * $matcher = array('id' => 'my_id'); + * + * // Matcher that asserts that there is a "span" tag. + * $matcher = array('tag' => 'span'); + * + * // Matcher that asserts that there is a "span" tag with the content + * // "Hello World". + * $matcher = array('tag' => 'span', 'content' => 'Hello World'); + * + * // Matcher that asserts that there is a "span" tag with content matching + * // the regular expression pattern. + * $matcher = array('tag' => 'span', 'content' => 'regexp:/Try P(HP|ython)/'); + * + * // Matcher that asserts that there is a "span" with an "list" class + * // attribute. + * $matcher = array( + * 'tag'=> 'span', + * 'attributes' => array('class' => 'list') + * ); + * + * // Matcher that asserts that there is a "span" inside of a "div". + * $matcher = array( + * 'tag'=> 'span', + * 'parent' => array('tag' => 'div') + * ); + * + * // Matcher that asserts that there is a "span" somewhere inside a + * // "table". + * $matcher = array( + * 'tag' => 'span', + * 'ancestor' => array('tag' => 'table') + * ); + * + * // Matcher that asserts that there is a "span" with at least one "em" + * // child. + * $matcher = array( + * 'tag' => 'span', + * 'child' => array('tag' => 'em') + * ); + * + * // Matcher that asserts that there is a "span" containing a (possibly + * // nested) "strong" tag. + * $matcher = array( + * 'tag'=> 'span', + * 'descendant' => array('tag' => 'strong') + * ); + * + * // Matcher that asserts that there is a "span" containing 5-10 "em" tags + * // as immediate children. + * $matcher = array( + * 'tag' => 'span', + * 'children' => array( + * 'less_than'=> 11, + * 'greater_than' => 4, + * 'only' => array('tag' => 'em') + * ) + * ); + * + * // Matcher that asserts that there is a "div", with an "ul" ancestor and + * // a "li" parent (with class="enum"), and containing a "span" descendant + * // that contains an element with id="my_test" and the text "Hello World". + * $matcher = array( + * 'tag'=> 'div', + * 'ancestor' => array('tag' => 'ul'), + * 'parent' => array( + * 'tag'=> 'li', + * 'attributes' => array('class' => 'enum') + * ), + * 'descendant' => array( + * 'tag' => 'span', + * 'child' => array( + * 'id' => 'my_test', + * 'content' => 'Hello World' + * ) + * ) + * ); + * + * // Use assertTag() to apply a $matcher to a piece of $html. + * $this->assertTag($matcher, $html); + * + * // Use assertTag() to apply a $matcher to a piece of $xml. + * $this->assertTag($matcher, $xml, '', FALSE); + * + * + * The second argument ($actual) is a string containing either HTML or + * XML text to be tested. + * + * The third argument ($message) is an optional message that will be + * used if the assertion fails. + * + * The fourth argument ($html) is an optional flag specifying whether + * to load the $actual string into a DOMDocument using the HTML or + * XML load strategy. It is TRUE by default, which assumes the HTML + * load strategy. In many cases, this will be acceptable for XML as well. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param boolean $isHtml + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ +function assertTag($matcher, $actual, $message = '', $isHtml = TRUE) +{ + return PHPUnit_Framework_Assert::assertTag($matcher, $actual, $message, $isHtml); +} + +/** + * Evaluates a PHPUnit_Framework_Constraint matcher object. + * + * @param mixed$value + * @param PHPUnit_Framework_Constraint $constraint + * @param string $message + * @since Method available since Release 3.0.0 + */ +function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '') +{ + return PHPUnit_Framework_Assert::assertThat($value, $constraint, $message); +} + +/** + * Asserts that a condition is true. + * + * @param boolean $condition + * @param string $message + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertTrue($condition, $message = '') +{ + return PHPUnit_Framework_Assert::assertTrue($condition, $message); +} + +/** + * Asserts that two XML files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '') +{ + return PHPUnit_Framework_Assert::assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message); +} + +/** + * Asserts that two XML files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '') +{ + return PHPUnit_Framework_Assert::assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message); +} + +/** + * Asserts that two XML documents are equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.3.0 + */ +function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '') +{ + return PHPUnit_Framework_Assert::assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message); +} + +/** + * Asserts that two XML documents are equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '') +{ + return PHPUnit_Framework_Assert::assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message); +} + +/** + * Asserts that two XML documents are not equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.3.0 + */ +function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '') +{ + return PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message); +} + +/** + * Asserts that two XML documents are not equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * @since Method available since Release 3.1.0 + */ +function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '') +{ + return PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message); +} + +/** + * Returns a matcher that matches when the method it is evaluated for + * is invoked at the given $index. + * + * @param integer $index + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex + * @since Method available since Release 3.0.0 + */ +function at($index) +{ + return PHPUnit_Framework_TestCase::at($index); +} + +/** + * Returns a matcher that matches when the method it is evaluated for + * is executed at least once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce + * @since Method available since Release 3.0.0 + */ +function atLeastOnce() +{ + return PHPUnit_Framework_TestCase::atLeastOnce(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Attribute matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_Attribute + * @since Method available since Release 3.1.0 + */ +function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName) +{ + return PHPUnit_Framework_Assert::attribute($constraint, $attributeName); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object + * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher + * object. + * + * @param string $attributeName + * @param mixed $value + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @return PHPUnit_Framework_Constraint_Attribute + * @since Method available since Release 3.1.0 + */ +function attributeEqualTo($attributeName, $value, $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::attributeEqualTo($attributeName, $value, $delta, $maxDepth, $canonicalize, $ignoreCase); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object. + * + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_ClassHasAttribute + * @since Method available since Release 3.1.0 + */ +function classHasAttribute($attributeName) +{ + return PHPUnit_Framework_Assert::classHasAttribute($attributeName); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher + * object. + * + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * @since Method available since Release 3.1.0 + */ +function classHasStaticAttribute($attributeName) +{ + return PHPUnit_Framework_Assert::classHasStaticAttribute($attributeName); +} + +/** + * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher + * object. + * + * @param mixed $value + * @param boolean $checkForObjectIdentity + * @return PHPUnit_Framework_Constraint_TraversableContains + * @since Method available since Release 3.0.0 + */ +function contains($value, $checkForObjectIdentity = TRUE) +{ + return PHPUnit_Framework_Assert::contains($value, $checkForObjectIdentity); +} + +/** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $type + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + * @since Method available since Release 3.1.4 + */ +function containsOnly($type) +{ + return PHPUnit_Framework_Assert::containsOnly($type); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object. + * + * @param mixed $value + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + * @return PHPUnit_Framework_Constraint_IsEqual + * @since Method available since Release 3.0.0 + */ +function equalTo($value, $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) +{ + return PHPUnit_Framework_Assert::equalTo($value, $delta, $maxDepth, $canonicalize, $ignoreCase); +} + +/** + * Returns a matcher that matches when the method it is evaluated for + * is executed exactly $count times. + * + * @param integer $count + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * @since Method available since Release 3.0.0 + */ +function exactly($count) +{ + return PHPUnit_Framework_TestCase::exactly($count); +} + +/** + * Returns a PHPUnit_Framework_Constraint_FileExists matcher object. + * + * @return PHPUnit_Framework_Constraint_FileExists + * @since Method available since Release 3.0.0 + */ +function fileExists() +{ + return PHPUnit_Framework_Assert::fileExists(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_GreaterThan + * @since Method available since Release 3.0.0 + */ +function greaterThan($value) +{ + return PHPUnit_Framework_Assert::greaterThan($value); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_Or + * @since Method available since Release 3.1.0 + */ +function greaterThanOrEqual($value) +{ + return PHPUnit_Framework_Assert::greaterThanOrEqual($value); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_IsIdentical + * @since Method available since Release 3.0.0 + */ +function identicalTo($value) +{ + return PHPUnit_Framework_Assert::identicalTo($value); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object. + * + * @return PHPUnit_Framework_Constraint_IsEmpty + * @since Method available since Release 3.5.0 + */ +function isEmpty() +{ + return PHPUnit_Framework_Assert::isEmpty(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object. + * + * @return PHPUnit_Framework_Constraint_IsFalse + * @since Method available since Release 3.3.0 + */ +function isFalse() +{ + return PHPUnit_Framework_Assert::isFalse(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object. + * + * @param string $className + * @return PHPUnit_Framework_Constraint_IsInstanceOf + * @since Method available since Release 3.0.0 + */ +function isInstanceOf($className) +{ + return PHPUnit_Framework_Assert::isInstanceOf($className); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsNull matcher object. + * + * @return PHPUnit_Framework_Constraint_IsNull + * @since Method available since Release 3.3.0 + */ +function isNull() +{ + return PHPUnit_Framework_Assert::isNull(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object. + * + * @return PHPUnit_Framework_Constraint_IsTrue + * @since Method available since Release 3.3.0 + */ +function isTrue() +{ + return PHPUnit_Framework_Assert::isTrue(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Callback matcher object. + * + * @return PHPUnit_Framework_Constraint_Callback + */ +function callback() +{ + return PHPUnit_Framework_Assert::callback(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsType matcher object. + * + * @param string $type + * @return PHPUnit_Framework_Constraint_IsType + * @since Method available since Release 3.0.0 + */ +function isType($type) +{ + return PHPUnit_Framework_Assert::isType($type); +} + +/** + * Returns a PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_LessThan + * @since Method available since Release 3.0.0 + */ +function lessThan($value) +{ + return PHPUnit_Framework_Assert::lessThan($value); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * @return PHPUnit_Framework_Constraint_Or + * @since Method available since Release 3.1.0 + */ +function lessThanOrEqual($value) +{ + return PHPUnit_Framework_Assert::lessThanOrEqual($value); +} + +/** + * Returns a PHPUnit_Framework_Constraint_And matcher object. + * + * @return PHPUnit_Framework_Constraint_And + * @since Method available since Release 3.0.0 + */ +function logicalAnd() +{ + return PHPUnit_Framework_Assert::logicalAnd(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Not matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * @return PHPUnit_Framework_Constraint_Not + * @since Method available since Release 3.0.0 + */ +function logicalNot(PHPUnit_Framework_Constraint $constraint) +{ + return PHPUnit_Framework_Assert::logicalNot($constraint); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Or matcher object. + * + * @return PHPUnit_Framework_Constraint_Or + * @since Method available since Release 3.0.0 + */ +function logicalOr() +{ + return PHPUnit_Framework_Assert::logicalOr(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Xor matcher object. + * + * @return PHPUnit_Framework_Constraint_Xor + * @since Method available since Release 3.0.0 + */ +function logicalXor() +{ + return PHPUnit_Framework_Assert::logicalXor(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object. + * + * @param string $string + * @return PHPUnit_Framework_Constraint_StringMatches + * @since Method available since Release 3.5.0 + */ +function matches($string) +{ + return PHPUnit_Framework_Assert::matches($string); +} + +/** + * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object. + * + * @param string $pattern + * @return PHPUnit_Framework_Constraint_PCREMatch + * @since Method available since Release 3.0.0 + */ +function matchesRegularExpression($pattern) +{ + return PHPUnit_Framework_Assert::matchesRegularExpression($pattern); +} + +/** + * Returns a matcher that matches when the method it is evaluated for + * is never executed. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * @since Method available since Release 3.0.0 + */ +function never() +{ + return PHPUnit_Framework_TestCase::never(); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object. + * + * @param string $attributeName + * @return PHPUnit_Framework_Constraint_ObjectHasAttribute + * @since Method available since Release 3.0.0 + */ +function objectHasAttribute($attributeName) +{ + return PHPUnit_Framework_Assert::objectHasAttribute($attributeName); +} + +/** + * + * + * @param mixed $value, ... + * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls + * @since Method available since Release 3.0.0 + */ +function onConsecutiveCalls() +{ + return PHPUnit_Framework_TestCase::onConsecutiveCalls(); +} + +/** + * Returns a matcher that matches when the method it is evaluated for + * is executed exactly once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * @since Method available since Release 3.0.0 + */ +function once() +{ + return PHPUnit_Framework_TestCase::once(); +} + +/** + * + * + * @param integer $argumentIndex + * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument + * @since Method available since Release 3.3.0 + */ +function returnArgument($argumentIndex) +{ + return PHPUnit_Framework_TestCase::returnArgument($argumentIndex); +} + +/** + * + * + * @param mixed $callback + * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback + * @since Method available since Release 3.3.0 + */ +function returnCallback($callback) +{ + return PHPUnit_Framework_TestCase::returnCallback($callback); +} + +/** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf + * @since Method available since Release 3.6.0 + */ +function returnSelf() +{ + return PHPUnit_Framework_TestCase::returnSelf(); +} + +/** + * + * + * @param mixed $value + * @return PHPUnit_Framework_MockObject_Stub_Return + * @since Method available since Release 3.0.0 + */ +function returnValue($value) +{ + return PHPUnit_Framework_TestCase::returnValue($value); +} + +/** + * + * + * @param array $valueMap + * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap + * @since Method available since Release 3.6.0 + */ +function returnValueMap(array $valueMap) +{ + return PHPUnit_Framework_TestCase::returnValueMap($valueMap); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringContains matcher object. + * + * @param string $string + * @param boolean $case + * @return PHPUnit_Framework_Constraint_StringContains + * @since Method available since Release 3.0.0 + */ +function stringContains($string, $case = TRUE) +{ + return PHPUnit_Framework_Assert::stringContains($string, $case); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object. + * + * @param mixed $suffix + * @return PHPUnit_Framework_Constraint_StringEndsWith + * @since Method available since Release 3.4.0 + */ +function stringEndsWith($suffix) +{ + return PHPUnit_Framework_Assert::stringEndsWith($suffix); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object. + * + * @param mixed $prefix + * @return PHPUnit_Framework_Constraint_StringStartsWith + * @since Method available since Release 3.4.0 + */ +function stringStartsWith($prefix) +{ + return PHPUnit_Framework_Assert::stringStartsWith($prefix); +} + +/** + * + * + * @param Exception $exception + * @return PHPUnit_Framework_MockObject_Stub_Exception + * @since Method available since Release 3.1.0 + */ +function throwException(Exception $exception) +{ + return PHPUnit_Framework_TestCase::throwException($exception); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php.in b/vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php.in new file mode 100644 index 0000000..ada7bfc --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php.in @@ -0,0 +1,44 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */{functions} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/AssertionFailedError.php b/vendor/phpunit/phpunit/PHPUnit/Framework/AssertionFailedError.php new file mode 100644 index 0000000..b3ab961 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/AssertionFailedError.php @@ -0,0 +1,68 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Thrown when an assertion failed. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_AssertionFailedError extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing +{ + /** + * Wrapper for getMessage() which is declared as final. + * + * @return string + */ + public function toString() + { + return $this->getMessage(); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator.php new file mode 100644 index 0000000..c63398b --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator.php @@ -0,0 +1,97 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Abstract base class for comparators which compare values for equality. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +abstract class PHPUnit_Framework_Comparator +{ + /** + * @var PHPUnit_Framework_ComparatorFactory + */ + protected $factory; + + /** + * @param PHPUnit_Framework_ComparatorFactory $factory + */ + public function setFactory(PHPUnit_Framework_ComparatorFactory $factory) + { + $this->factory = $factory; + } + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + abstract public function accepts($expected, $actual); + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + abstract public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Array.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Array.php new file mode 100644 index 0000000..9e3d4ab --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Array.php @@ -0,0 +1,177 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares arrays for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Array extends PHPUnit_Framework_Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return is_array($expected) && is_array($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE, array &$processed = array()) + { + if ($canonicalize) { + sort($expected); + sort($actual); + } + + $remaining = $actual; + $expString = $actString = "Array (\n"; + $equal = TRUE; + + foreach ($expected as $key => $value) { + unset($remaining[$key]); + + if (!array_key_exists($key, $actual)) { + $expString .= sprintf( + " %s => %s\n", + + PHPUnit_Util_Type::export($key), + PHPUnit_Util_Type::shortenedExport($value) + ); + $equal = FALSE; + continue; + } + + try { + $this->factory->getComparatorFor($value, $actual[$key])->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); + $expString .= sprintf( + " %s => %s\n", + + PHPUnit_Util_Type::export($key), + PHPUnit_Util_Type::shortenedExport($value) + ); + $actString .= sprintf( + " %s => %s\n", + + PHPUnit_Util_Type::export($key), + PHPUnit_Util_Type::shortenedExport($actual[$key]) + ); + } + + catch (PHPUnit_Framework_ComparisonFailure $e) { + $expString .= sprintf( + " %s => %s\n", + + PHPUnit_Util_Type::export($key), + $e->getExpectedAsString() + ? $this->indent($e->getExpectedAsString()) + : PHPUnit_Util_Type::shortenedExport($e->getExpected()) + ); + $actString .= sprintf( + " %s => %s\n", + + PHPUnit_Util_Type::export($key), + $e->getActualAsString() + ? $this->indent($e->getActualAsString()) + : PHPUnit_Util_Type::shortenedExport($e->getActual()) + ); + $equal = FALSE; + } + } + + foreach ($remaining as $key => $value) { + $actString .= sprintf( + " %s => %s\n", + + PHPUnit_Util_Type::export($key), + PHPUnit_Util_Type::shortenedExport($value) + ); + $equal = FALSE; + } + + $expString .= ')'; + $actString .= ')'; + + if (!$equal) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + $expString, + $actString, + FALSE, + 'Failed asserting that two arrays are equal.' + ); + } + } + + protected function indent($lines) + { + return trim(str_replace("\n", "\n ", $lines)); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/DOMDocument.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/DOMDocument.php new file mode 100644 index 0000000..400204e --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/DOMDocument.php @@ -0,0 +1,114 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares DOMDocument instances for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_DOMDocument extends PHPUnit_Framework_Comparator_Object +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return $expected instanceof DOMDocument && $actual instanceof DOMDocument; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if ($expected->C14N() !== $actual->C14N()) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + $this->domToText($expected), + $this->domToText($actual), + FALSE, + 'Failed asserting that two DOM documents are equal.' + ); + } + } + + /** + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMDocument. + * + * @param DOMDocument $document + * @return string + */ + protected function domToText(DOMDocument $document) + { + $document->formatOutput = TRUE; + $document->normalizeDocument(); + + return $document->saveXML(); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Double.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Double.php new file mode 100644 index 0000000..e2d8d77 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Double.php @@ -0,0 +1,101 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares doubles for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Double extends PHPUnit_Framework_Comparator_Numeric +{ + /** + * Smallest value available in PHP. + * + * @var float + */ + const EPSILON = 0.0000000001; + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return (is_double($expected) || is_double($actual)) && is_numeric($expected) && is_numeric($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if ($delta == 0) { + $delta = self::EPSILON; + } + + parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Exception.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Exception.php new file mode 100644 index 0000000..e4b2e97 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Exception.php @@ -0,0 +1,92 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares Exception instances for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Exception extends PHPUnit_Framework_Comparator_Object +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return $expected instanceof Exception && $actual instanceof Exception; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset( + $array['file'], + $array['line'], + $array['trace'], + $array['string'], // some internal property of Exception + $array['xdebug_message'] // some internal property added by XDebug + ); + + return $array; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/MockObject.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/MockObject.php new file mode 100644 index 0000000..52af9b1 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/MockObject.php @@ -0,0 +1,86 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_MockObject extends PHPUnit_Framework_Comparator_Object +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return $expected instanceof PHPUnit_Framework_MockObject_MockObject && $actual instanceof PHPUnit_Framework_MockObject_MockObject; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset($array['__phpunit_invocationMocker']); + + return $array; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Numeric.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Numeric.php new file mode 100644 index 0000000..2ab9b06 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Numeric.php @@ -0,0 +1,116 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares numerical values for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @author Alexander + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Numeric extends PHPUnit_Framework_Comparator_Scalar +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + // all numerical values, but not if one of them is a double + return is_numeric($expected) && is_numeric($actual) && !(is_double($expected) || is_double($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if (is_infinite($actual) && is_infinite($expected)) { + return; + } + + if (is_nan($actual) && is_nan($expected)) { + return; + } + + if ((is_infinite($actual) XOR is_infinite($expected)) || + (is_nan($actual) XOR is_nan($expected)) || + abs($actual - $expected) > $delta) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + '', + '', + FALSE, + sprintf( + 'Failed asserting that %s matches expected %s.', + + PHPUnit_Util_Type::export($actual), + PHPUnit_Util_Type::export($expected) + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Object.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Object.php new file mode 100644 index 0000000..cba32c8 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Object.php @@ -0,0 +1,145 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares objects for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Object extends PHPUnit_Framework_Comparator_Array +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return is_object($expected) && is_object($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE, array &$processed = array()) + { + if (get_class($actual) !== get_class($expected)) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + PHPUnit_Util_Type::export($expected), + PHPUnit_Util_Type::export($actual), + FALSE, + sprintf( + '%s is not instance of expected class "%s".', + + PHPUnit_Util_Type::export($actual), + get_class($expected) + ) + ); + } + + // don't compare twice to allow for cyclic dependencies + if (in_array(array($actual, $expected), $processed, TRUE) || + in_array(array($expected, $actual), $processed, TRUE)) { + return; + } + + $processed[] = array($actual, $expected); + + // don't compare objects if they are identical + // this helps to avoid the error "maximum function nesting level reached" + // CAUTION: this conditional clause is not tested + if ($actual !== $expected) { + try { + parent::assertEquals($this->toArray($expected), $this->toArray($actual), $delta, $canonicalize, $ignoreCase, $processed); + } + + catch (PHPUnit_Framework_ComparisonFailure $e) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + // replace "Array" with "MyClass object" + substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5), + substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5), + FALSE, + 'Failed asserting that two objects are equal.' + ); + } + } + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + return PHPUnit_Util_Type::toArray($object); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Resource.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Resource.php new file mode 100644 index 0000000..30f751f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Resource.php @@ -0,0 +1,97 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares resources for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Resource extends PHPUnit_Framework_Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return is_resource($expected) && is_resource($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if ($actual != $expected) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + PHPUnit_Util_Type::export($expected), + PHPUnit_Util_Type::export($actual) + ); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Scalar.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Scalar.php new file mode 100644 index 0000000..e13bf75 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Scalar.php @@ -0,0 +1,136 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares scalar or NULL values for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Scalar extends PHPUnit_Framework_Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + * @since Method available since Release 3.6.0 + */ + public function accepts($expected, $actual) + { + return ((is_scalar($expected) XOR NULL === $expected) && + (is_scalar($actual) XOR NULL === $actual)) + // allow comparison between strings and objects featuring __toString() + || (is_string($expected) && is_object($actual) && method_exists($actual, '__toString')) + || (is_object($expected) && method_exists($expected, '__toString') && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + $expectedToCompare = $expected; + $actualToCompare = $actual; + + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (is_string($expected) || is_string($actual)) { + $expectedToCompare = (string)$expectedToCompare; + $actualToCompare = (string)$actualToCompare; + + if ($ignoreCase) { + $expectedToCompare = strtolower($expectedToCompare); + $actualToCompare = strtolower($actualToCompare); + } + } + + if ($expectedToCompare != $actualToCompare) { + if (is_string($expected) && is_string($actual)) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + PHPUnit_Util_Type::export($expected), + PHPUnit_Util_Type::export($actual), + FALSE, + 'Failed asserting that two strings are equal.' + ); + } + + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + FALSE, + sprintf( + 'Failed asserting that %s matches expected %s.', + + PHPUnit_Util_Type::export($actual), + PHPUnit_Util_Type::export($expected) + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/SplObjectStorage.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/SplObjectStorage.php new file mode 100644 index 0000000..efbf2c7 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/SplObjectStorage.php @@ -0,0 +1,114 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares SplObjectStorage instances for equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_SplObjectStorage extends PHPUnit_Framework_Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + PHPUnit_Util_Type::export($expected), + PHPUnit_Util_Type::export($actual), + FALSE, + 'Failed asserting that two objects are equal.' + ); + } + } + + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + PHPUnit_Util_Type::export($expected), + PHPUnit_Util_Type::export($actual), + FALSE, + 'Failed asserting that two objects are equal.' + ); + } + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Type.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Type.php new file mode 100644 index 0000000..5cfcf56 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Comparator/Type.php @@ -0,0 +1,105 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Compares values for type equality. + * + * @package PHPUnit + * @subpackage Framework_Comparator + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Comparator_Type extends PHPUnit_Framework_Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return boolean + */ + public function accepts($expected, $actual) + { + return TRUE; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws PHPUnit_Framework_ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if (gettype($expected) != gettype($actual)) { + throw new PHPUnit_Framework_ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + FALSE, + sprintf( + '%s does not match expected type "%s".', + + PHPUnit_Util_Type::shortenedExport($actual), + gettype($expected) + ) + ); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/ComparatorFactory.php b/vendor/phpunit/phpunit/PHPUnit/Framework/ComparatorFactory.php new file mode 100644 index 0000000..7a1ecb0 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/ComparatorFactory.php @@ -0,0 +1,156 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Factory for comparators which compare values for equality. + * + * @package PHPUnit + * @subpackage Framework + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_ComparatorFactory +{ + /** + * @var array + */ + protected $comparators = array(); + + /** + * @var PHPUnit_Framework_ComparatorFactory + */ + private static $defaultInstance = NULL; + + /** + * Constructs a new factory. + */ + public function __construct() + { + $this->register(new PHPUnit_Framework_Comparator_Type); + $this->register(new PHPUnit_Framework_Comparator_Scalar); + $this->register(new PHPUnit_Framework_Comparator_Numeric); + $this->register(new PHPUnit_Framework_Comparator_Double); + $this->register(new PHPUnit_Framework_Comparator_Array); + $this->register(new PHPUnit_Framework_Comparator_Resource); + $this->register(new PHPUnit_Framework_Comparator_Object); + $this->register(new PHPUnit_Framework_Comparator_Exception); + $this->register(new PHPUnit_Framework_Comparator_SplObjectStorage); + $this->register(new PHPUnit_Framework_Comparator_DOMDocument); + $this->register(new PHPUnit_Framework_Comparator_MockObject); + } + + /** + * Returns the default instance. + * + * @return PHPUnit_Framework_ComparatorFactory + */ + public static function getDefaultInstance() + { + if (self::$defaultInstance === NULL) { + self::$defaultInstance = new PHPUnit_Framework_ComparatorFactory; + } + + return self::$defaultInstance; + } + + /** + * Returns the correct comparator for comparing two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return PHPUnit_Framework_Comparator + * @throws PHPUnit_Framework_Exception + */ + public function getComparatorFor($expected, $actual) + { + foreach ($this->comparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + + throw new PHPUnit_Framework_Exception( + sprintf( + 'No comparator is registered for comparing the types "%s" and "%s"', + gettype($expected), gettype($actual) + ) + ); + } + + /** + * Registers a new comparator. + * + * This comparator will be returned by getInstance() if its accept() method + * returns TRUE for the compared values. It has higher priority than the + * existing comparators, meaning that its accept() method will be tested + * before those of the other comparators. + * + * @param PHPUnit_Framework_Comparator $comparator The registered comparator + */ + public function register(PHPUnit_Framework_Comparator $comparator) + { + array_unshift($this->comparators, $comparator); + $comparator->setFactory($this); + } + + /** + * Unregisters a comparator. + * + * This comparator will no longer be returned by getInstance(). + * + * @param PHPUnit_Framework_Comparator $comparator The unregistered comparator + */ + public function unregister(PHPUnit_Framework_Comparator $comparator) + { + foreach ($this->comparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->comparators[$key]); + } + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/ComparisonFailure.php b/vendor/phpunit/phpunit/PHPUnit/Framework/ComparisonFailure.php new file mode 100644 index 0000000..5482337 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/ComparisonFailure.php @@ -0,0 +1,166 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Thrown when an assertion for string equality failed. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_ComparisonFailure extends PHPUnit_Framework_AssertionFailedError +{ + /** + * Expected value of the retrieval which does not match $actual. + * @var mixed + */ + protected $expected; + + /** + * Actually retrieved value which does not match $expected. + * @var mixed + */ + protected $actual; + + /** + * The string representation of the expected value + * @var string + */ + protected $expectedAsString; + + /** + * The string representation of the actual value + * @var string + */ + protected $actualAsString; + + /** + * @var boolean + */ + protected $identical; + + /** + * Optional message which is placed in front of the first line + * returned by toString(). + * @var string + */ + protected $message; + + /** + * Initialises with the expected value and the actual value. + * + * @param mixed $expected Expected value retrieved. + * @param mixed $actual Actual value retrieved. + * @param string $expectedAsString + * @param string $actualAsString + * @param boolean $identical + * @param string $message A string which is prefixed on all returned lines + * in the difference output. + */ + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = FALSE, $message = '') + { + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; + } + + /** + * @return mixed + */ + public function getActual() + { + return $this->actual; + } + + /** + * @return mixed + */ + public function getExpected() + { + return $this->expected; + } + + /** + * @return string + */ + public function getActualAsString() + { + return $this->actualAsString; + } + + /** + * @return string + */ + public function getExpectedAsString() + { + return $this->expectedAsString; + } + + /** + * @return string + */ + public function getDiff() + { + return $this->actualAsString || $this->expectedAsString + ? PHPUnit_Util_Diff::diff($this->expectedAsString, $this->actualAsString) + : ''; + } + + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint.php new file mode 100644 index 0000000..dcb6ca3 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint.php @@ -0,0 +1,180 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Abstract base class for constraints. which are placed upon any value. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 3.0.0 + */ +abstract class PHPUnit_Framework_Constraint implements Countable, PHPUnit_Framework_SelfDescribing +{ + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $success = FALSE; + + if ($this->matches($other)) { + $success = TRUE; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return FALSE; + } + + /** + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.4.0 + */ + public function count() + { + return 1; + } + + /** + * Throws an exception for the given compared value and test description + * + * @param mixed $other Evaluated value or object. + * @param string $description Additional information about the test + * @param PHPUnit_Framework_ComparisonFailure $comparisonFailure + * @throws PHPUnit_Framework_ExpectationFailedException + */ + protected function fail($other, $description, PHPUnit_Framework_ComparisonFailure $comparisonFailure = NULL) + { + $failureDescription = sprintf( + 'Failed asserting that %s.', + $this->failureDescription($other) + ); + + $additionalFailureDescription = $this->additionalFailureDescription($other); + if ($additionalFailureDescription) { + $failureDescription .= "\n" . $additionalFailureDescription; + } + + if (!empty($description)) { + $failureDescription = $description . "\n" . $failureDescription; + } + + throw new PHPUnit_Framework_ExpectationFailedException( + $failureDescription, + $comparisonFailure + ); + } + + /** + * Return additional failure description where needed + * + * The function can be overridden to provide additional failure + * information like a diff + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function additionalFailureDescription($other) + { + return ""; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * To provide additional failure information additionalFailureDescription + * can be used. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return PHPUnit_Util_Type::export($other) . ' ' . $this->toString(); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/And.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/And.php new file mode 100644 index 0000000..d8d0337 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/And.php @@ -0,0 +1,164 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Logical AND. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_And extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint[] + */ + protected $constraints = array(); + + /** + * @var PHPUnit_Framework_Constraint + */ + protected $lastConstraint = NULL; + + /** + * @param PHPUnit_Framework_Constraint[] $constraints + * @throws PHPUnit_Framework_Exception + */ + public function setConstraints(array $constraints) + { + $this->constraints = array(); + + foreach ($constraints as $key => $constraint) { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + throw new PHPUnit_Framework_Exception( + 'All parameters to ' . __CLASS__ . + ' must be a constraint object.' + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $success = TRUE; + $constraint = NULL; + + foreach ($this->constraints as $constraint) { + if (!$constraint->evaluate($other, $description, TRUE)) { + $success = FALSE; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' and '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.4.0 + */ + public function count() + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + + return $count; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ArrayHasKey.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ArrayHasKey.php new file mode 100644 index 0000000..443eb36 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ArrayHasKey.php @@ -0,0 +1,114 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the array it is evaluated for has a given key. + * + * Uses array_key_exists() to check if the key is found in the input array, if + * not found the evaluaton fails. + * + * The array key is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_ArrayHasKey extends PHPUnit_Framework_Constraint +{ + /** + * @var integer|string + */ + protected $key; + + /** + * @param integer|string $key + */ + public function __construct($key) + { + $this->key = $key; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return array_key_exists($this->key, $other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'has the key ' . PHPUnit_Util_Type::export($this->key); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return 'an array ' . $this->toString(); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Attribute.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Attribute.php new file mode 100644 index 0000000..d47ef72 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Attribute.php @@ -0,0 +1,129 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.0 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.0 + */ + +class PHPUnit_Framework_Constraint_Attribute extends PHPUnit_Framework_Constraint_Composite +{ + /** + * @var string + */ + protected $attributeName; + + /** + * @param PHPUnit_Framework_Constraint $constraint + * @param string $attributeName + */ + public function __construct(PHPUnit_Framework_Constraint $constraint, $attributeName) + { + parent::__construct($constraint); + + $this->attributeName = $attributeName; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + return parent::evaluate( + PHPUnit_Framework_Assert::readAttribute( + $other, $this->attributeName + ), + $description, + $returnResult + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'attribute "' . $this->attributeName . '" ' . + $this->innerConstraint->toString(); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Callback.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Callback.php new file mode 100644 index 0000000..7a3340d --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Callback.php @@ -0,0 +1,116 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + */ + +/** + * Constraint that evaluates against a specified closure. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Timon Rapp + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + */ +class PHPUnit_Framework_Constraint_Callback extends PHPUnit_Framework_Constraint +{ + private $callback; + + /** + * @param callable $value + * @throws InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException( + sprintf( + 'Specified callback <%s> is not callable.', + $this->callbackToString($callback) + ) + ); + } + $this->callback = $callback; + } + + /** + * Evaluates the constraint for parameter $value. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $value Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return call_user_func($this->callback, $other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is accepted by specified callback'; + } + + private function callbackToString($callback) + { + if (!is_array($callback)) { + return $callback; + } + if (empty($callback)) { + return "empty array"; + } + if (!isset($callback[0]) || !isset($callback[1])) { + return "array without indexes 0 and 1 set"; + } + if (is_object($callback[0])) { + $callback[0] = get_class($callback[0]); + } + return $callback[0] . '::' . $callback[1]; + } + +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasAttribute.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasAttribute.php new file mode 100644 index 0000000..5a6c2e5 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasAttribute.php @@ -0,0 +1,124 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.0 + */ + +/** + * Constraint that asserts that the class it is evaluated for has a given + * attribute. + * + * The attribute name is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_Constraint_ClassHasAttribute extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $attributeName; + + /** + * @param string $attributeName + */ + public function __construct($attributeName) + { + $this->attributeName = $attributeName; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + $class = new ReflectionClass($other); + + return $class->hasProperty($this->attributeName); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'has attribute "%s"', + + $this->attributeName + ); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%sclass "%s" %s', + + is_object($other) ? 'object of ' : '', + is_object($other) ? get_class($other) : $other, + $this->toString() + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php new file mode 100644 index 0000000..e9667fc --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php @@ -0,0 +1,98 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.0 + */ + +/** + * Constraint that asserts that the class it is evaluated for has a given + * static attribute. + * + * The attribute name is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_Constraint_ClassHasStaticAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + $class = new ReflectionClass($other); + + if ($class->hasProperty($this->attributeName)) { + $attribute = $class->getProperty($this->attributeName); + + return $attribute->isStatic(); + } else { + return FALSE; + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + * @since Method available since Release 3.3.0 + */ + public function toString() + { + return sprintf( + 'has static attribute "%s"', + + $this->attributeName + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Composite.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Composite.php new file mode 100644 index 0000000..a533f29 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Composite.php @@ -0,0 +1,114 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ + +abstract class PHPUnit_Framework_Constraint_Composite extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $innerConstraint; + + /** + * @param PHPUnit_Framework_Constraint $innerConstraint + * @param string $attributeName + */ + public function __construct(PHPUnit_Framework_Constraint $innerConstraint) + { + $this->innerConstraint = $innerConstraint; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + try { + return $this->innerConstraint->evaluate( + $other, + $description, + $returnResult + ); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->fail($other, $description); + } + } + + /** + * Counts the number of constraint elements. + * + * @return integer + */ + public function count() + { + return count($this->innerConstraint); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Count.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Count.php new file mode 100644 index 0000000..e3c9f0c --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Count.php @@ -0,0 +1,128 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Constraint_Count extends PHPUnit_Framework_Constraint +{ + /** + * @var integer + */ + protected $expectedCount = 0; + + /** + * @param integer $expected + */ + public function __construct($expected) + { + $this->expectedCount = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other + * @return boolean + */ + protected function matches($other) + { + return $this->expectedCount === $this->getCountOf($other); + } + + /** + * @param mixed $other + * @return boolean + */ + protected function getCountOf($other) + { + if ($other instanceof Countable || is_array($other)) { + return count($other); + } + + else if ($other instanceof Iterator) { + return iterator_count($other); + } + } + + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + 'actual size %d matches expected size %d', + + $this->getCountOf($other), + $this->expectedCount + ); + } + + /** + * @return string + */ + public function toString() + { + return 'count matches '; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Exception.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Exception.php new file mode 100644 index 0000000..4158589 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Exception.php @@ -0,0 +1,129 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.6 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.6 + */ +class PHPUnit_Framework_Constraint_Exception extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $className; + + /** + * @param string $className + */ + public function __construct($className) + { + $this->className = $className; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $other instanceof $this->className; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + if ($other !== NULL) { + $message = ''; + if ($other instanceof Exception && $other->getMessage()) { + $message = '. Message was: "' . $other->getMessage() . '"'; + } + return sprintf( + 'exception of type "%s" matches expected exception "%s"%s', + + get_class($other), + $this->className, + $message + ); + } + + return sprintf( + 'exception of type "%s" is thrown', + + $this->className + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'exception of type "%s"', + + $this->className + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionCode.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionCode.php new file mode 100644 index 0000000..5105657 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionCode.php @@ -0,0 +1,109 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.6 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.6 + */ +class PHPUnit_Framework_Constraint_ExceptionCode extends PHPUnit_Framework_Constraint +{ + /** + * @var integer + */ + protected $expectedCode; + + /** + * @param integer $expected + */ + public function __construct($expected) + { + $this->expectedCode = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param Exception $other + * @return boolean + */ + protected function matches($other) + { + return (string)$other->getCode() == (string)$this->expectedCode; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%s is equal to expected exception code %s', + PHPUnit_Util_Type::export($other->getCode()), + PHPUnit_Util_Type::export($this->expectedCode) + ); + } + + /** + * @return string + */ + public function toString() + { + return 'exception code is '; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionMessage.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionMessage.php new file mode 100644 index 0000000..64263dc --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionMessage.php @@ -0,0 +1,109 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.6 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.6 + */ +class PHPUnit_Framework_Constraint_ExceptionMessage extends PHPUnit_Framework_Constraint +{ + /** + * @var integer + */ + protected $expectedMessage; + + /** + * @param string $expected + */ + public function __construct($expected) + { + $this->expectedMessage = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param Exception $other + * @return boolean + */ + protected function matches($other) + { + return strpos($other->getMessage(), $this->expectedMessage) !== FALSE; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + "exception message '%s' contains '%s'", + $other->getMessage(), + $this->expectedMessage + ); + } + + /** + * @return string + */ + public function toString() + { + return 'exception message contains '; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/FileExists.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/FileExists.php new file mode 100644 index 0000000..f1e4908 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/FileExists.php @@ -0,0 +1,102 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that checks if the file(name) that it is evaluated for exists. + * + * The file path to check is passed as $other in evaluate(). + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_FileExists extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return file_exists($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + 'file "%s" exists', + + $other + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'file exists'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/GreaterThan.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/GreaterThan.php new file mode 100644 index 0000000..dc78aa2 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/GreaterThan.php @@ -0,0 +1,96 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the value it is evaluated for is greater + * than a given value. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_GreaterThan extends PHPUnit_Framework_Constraint +{ + /** + * @var numeric + */ + protected $value; + + /** + * @param numeric $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $this->value < $other; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is greater than ' . PHPUnit_Util_Type::export($this->value); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsAnything.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsAnything.php new file mode 100644 index 0000000..9ace7f0 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsAnything.php @@ -0,0 +1,102 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that accepts any input value. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsAnything extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + return $returnResult ? TRUE : NULL; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is anything'; + } + + /** + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.5.0 + */ + public function count() + { + return 0; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEmpty.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEmpty.php new file mode 100644 index 0000000..a3bd9f8 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEmpty.php @@ -0,0 +1,104 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */ + +/** + * Constraint that checks whether a variable is empty(). + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.0 + */ +class PHPUnit_Framework_Constraint_IsEmpty extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return empty($other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is empty'; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + $type = gettype($other); + + return sprintf( + '%s %s %s', + + $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a', + $type, + $this->toString() + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEqual.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEqual.php new file mode 100644 index 0000000..76a8624 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEqual.php @@ -0,0 +1,215 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Kore Nordmann + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that checks if one value is equal to another. + * + * Equality is checked with PHP's == operator, the operator is explained in + * detail at {@url http://www.php.net/manual/en/types.comparisons.php}. + * Two values are equal if they have the same value disregarding type. + * + * The expected value is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Kore Nordmann + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsEqual extends PHPUnit_Framework_Constraint +{ + /** + * @var mixed + */ + protected $value; + + /** + * @var float + */ + protected $delta = 0; + + /** + * @var integer + */ + protected $maxDepth = 10; + + /** + * @var boolean + */ + protected $canonicalize = FALSE; + + /** + * @var boolean + */ + protected $ignoreCase = FALSE; + + /** + * @var PHPUnit_Framework_ComparisonFailure + */ + protected $lastFailure; + + /** + * @param mixed $value + * @param float $delta + * @param integer $maxDepth + * @param boolean $canonicalize + * @param boolean $ignoreCase + */ + public function __construct($value, $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) + { + if (!is_numeric($delta)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'numeric'); + } + + if (!is_int($maxDepth)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'integer'); + } + + if (!is_bool($canonicalize)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean'); + } + + if (!is_bool($ignoreCase)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'boolean'); + } + + $this->value = $value; + $this->delta = $delta; + $this->maxDepth = $maxDepth; + $this->canonicalize = $canonicalize; + $this->ignoreCase = $ignoreCase; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $comparatorFactory = PHPUnit_Framework_ComparatorFactory::getDefaultInstance(); + + try { + $comparator = $comparatorFactory->getComparatorFor( + $other, $this->value + ); + + $comparator->assertEquals( + $this->value, + $other, + $this->delta, + $this->canonicalize, + $this->ignoreCase + ); + } + + catch (PHPUnit_Framework_ComparisonFailure $f) { + if ($returnResult) { + return FALSE; + } + + throw new PHPUnit_Framework_ExpectationFailedException( + trim($description . "\n" . $f->getMessage()), + $f + ); + } + + return TRUE; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $delta = ''; + + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== FALSE) { + return 'is equal to '; + } else { + return sprintf( + 'is equal to ', + + $this->value + ); + } + } else { + if ($this->delta != 0) { + $delta = sprintf( + ' with delta <%F>', + + $this->delta + ); + } + + return sprintf( + 'is equal to %s%s', + + PHPUnit_Util_Type::export($this->value), + $delta + ); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsFalse.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsFalse.php new file mode 100644 index 0000000..d232008 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsFalse.php @@ -0,0 +1,82 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * Constraint that accepts FALSE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Constraint_IsFalse extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $other === FALSE; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is false'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsIdentical.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsIdentical.php new file mode 100644 index 0000000..604258b --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsIdentical.php @@ -0,0 +1,172 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that one value is identical to another. + * + * Identical check is performed with PHP's === operator, the operator is + * explained in detail at + * {@url http://www.php.net/manual/en/types.comparisons.php}. + * Two values are identical if they have the same value and are of the same + * type. + * + * The expected value is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsIdentical extends PHPUnit_Framework_Constraint +{ + /** + * @var double + */ + const EPSILON = 0.0000000001; + + /** + * @var mixed + */ + protected $value; + + /** + * @param mixed $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + if (is_double($this->value) && is_double($other) && + !is_infinite($this->value) && !is_infinite($other)) { + $success = abs($this->value - $other) < self::EPSILON; + } + + else { + $success = $this->value === $other; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $f = NULL; + + // if both values are strings, make sure a diff is generated + if (is_string($this->value) && is_string($other)) { + $f = new PHPUnit_Framework_ComparisonFailure( + $this->value, + $other, + $this->value, + $other + ); + } + + $this->fail($other, $description, $f); + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + if (is_object($this->value) && is_object($other)) { + return 'two variables reference the same object'; + } + + if (is_string($this->value) && is_string($other)) { + return 'two strings are identical'; + } + + return parent::failureDescription($other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + if (is_object($this->value)) { + return 'is identical to an object of class "' . + get_class($this->value) . '"'; + } else { + return 'is identical to ' . + PHPUnit_Util_Type::export($this->value); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsInstanceOf.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsInstanceOf.php new file mode 100644 index 0000000..d590f12 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsInstanceOf.php @@ -0,0 +1,121 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the object it is evaluated for is an instance + * of a given class. + * + * The expected class name is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsInstanceOf extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $className; + + /** + * @param string $className + */ + public function __construct($className) + { + $this->className = $className; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return ($other instanceof $this->className); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%s is an instance of class "%s"', + + PHPUnit_Util_Type::shortenedExport($other), + $this->className + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'is instance of class "%s"', + + $this->className + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsNull.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsNull.php new file mode 100644 index 0000000..784f1ad --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsNull.php @@ -0,0 +1,82 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * Constraint that accepts NULL. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Constraint_IsNull extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $other === NULL; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is null'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsTrue.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsTrue.php new file mode 100644 index 0000000..262da8e --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsTrue.php @@ -0,0 +1,82 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * Constraint that accepts TRUE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $other === TRUE; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is true'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsType.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsType.php new file mode 100644 index 0000000..0fad628 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/IsType.php @@ -0,0 +1,190 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the value it is evaluated for is of a + * specified type. + * + * The expected value is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint +{ + const TYPE_ARRAY = 'array'; + const TYPE_BOOL = 'bool'; + const TYPE_FLOAT = 'float'; + const TYPE_INT = 'int'; + const TYPE_NULL = 'null'; + const TYPE_NUMERIC = 'numeric'; + const TYPE_OBJECT = 'object'; + const TYPE_RESOURCE = 'resource'; + const TYPE_STRING = 'string'; + const TYPE_SCALAR = 'scalar'; + const TYPE_CALLABLE = 'callable'; + + /** + * @var array + */ + protected $types = array( + 'array' => TRUE, + 'boolean' => TRUE, + 'bool' => TRUE, + 'float' => TRUE, + 'integer' => TRUE, + 'int' => TRUE, + 'null' => TRUE, + 'numeric' => TRUE, + 'object' => TRUE, + 'resource' => TRUE, + 'string' => TRUE, + 'scalar' => TRUE, + 'callable' => TRUE + ); + + /** + * @var string + */ + protected $type; + + /** + * @param string $type + * @throws PHPUnit_Framework_Exception + */ + public function __construct($type) + { + if (!isset($this->types[$type])) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Type specified for PHPUnit_Framework_Constraint_IsType <%s> ' . + 'is not a valid type.', + $type + ) + ); + } + + $this->type = $type; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + switch ($this->type) { + case 'numeric': { + return is_numeric($other); + } + + case 'integer': + case 'int': { + return is_integer($other); + } + + case 'float': { + return is_float($other); + } + + case 'string': { + return is_string($other); + } + + case 'boolean': + case 'bool': { + return is_bool($other); + } + + case 'null': { + return is_null($other); + } + + case 'array': { + return is_array($other); + } + + case 'object': { + return is_object($other); + } + + case 'resource': { + return is_resource($other); + } + + case 'scalar': { + return is_scalar($other); + } + + case 'callable': { + return is_callable($other); + } + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'is of type "%s"', + + $this->type + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches.php new file mode 100644 index 0000000..01bb70b --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches.php @@ -0,0 +1,126 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Bastian Feder + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since File available since Release 3.7.0 + */ + +/** + * Asserts whether or not two JSON objects are equal. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Bastian Feder + * @copyright 2011 Bastian Feder + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since Class available since Release 3.7.0 + */ +class PHPUnit_Framework_Constraint_JsonMatches extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $value; + + /** + * Creates a new constraint. + * + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + $decodedOther = json_decode($other); + if (!is_object($decodedOther)) { + $this->failure_reason = $this->getJsonError(); + return FALSE; + } + + $decodedValue = json_decode($this->value); + if (!is_object($decodedValue)) { + $this->failure_reason = $this->getJsonError(); + return FALSE; + } + + return $decodedOther == $decodedValue; + } + + /** + * Finds the last occurd JSON error. + * + * @param string $messagePrefix + * @return string The last JSON error prefixed with $messagePrefix. + */ + protected function getJsonError($messagePrefix = 'Json error!') + { + return PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + json_last_error(), + $messagePrefix + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return sprintf( + 'matches JSON string "%s"', + $this->value + ); + } +} \ No newline at end of file diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php new file mode 100644 index 0000000..5a984db --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php @@ -0,0 +1,106 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Bastian Feder + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since File available since Release 3.7.0 + */ + +/** + * Provides human readable messages for each JSON error. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Bastian Feder + * @copyright 2011 Bastian Feder + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since Class available since Release 3.7.0 + */ +class PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider +{ + /** + * Translatets accourd JSON error to a human readable string. + * + * @param string $error + * @return string + */ + public static function determineJsonError($error, $prefix = '') + { + switch (strtoupper($error)) { + case 'JSON_ERROR_NONE': + return; + case 'JSON_ERROR_DEPTH': + return $prefix . 'Maximum stack depth exceeded'; + case 'JSON_ERROR_STATE_MISMATCH': + return $prefix . 'Underflow or the modes mismatch'; + case 'JSON_ERROR_CTRL_CHAR': + return $prefix . 'Unexpected control character found'; + case 'JSON_ERROR_SYNTAX': + return $prefix . 'Syntax error, malformed JSON'; + case 'JSON_ERROR_UTF8': + return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return $prefix . 'Unknown error'; + } + } + + /** + * Translates a given type to a human readable message prefix. + * + * @param string $type + * @return string + */ + public static function translateTypeToPrefix($type) + { + switch (strtolower($type)) { + case 'expected': + $prefix = 'Expected value JSON decode error - '; + break; + case 'actual': + $prefix = 'Actual value JSON decode error - '; + break; + default: + $prefix = ''; + break; + } + return $prefix; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/LessThan.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/LessThan.php new file mode 100644 index 0000000..27eaa1f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/LessThan.php @@ -0,0 +1,96 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the value it is evaluated for is less than + * a given value. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_LessThan extends PHPUnit_Framework_Constraint +{ + /** + * @var numeric + */ + protected $value; + + /** + * @param numeric $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $this->value > $other; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is less than ' . PHPUnit_Util_Type::export($this->value); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Not.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Not.php new file mode 100644 index 0000000..c839487 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Not.php @@ -0,0 +1,203 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Logical NOT. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ + +class PHPUnit_Framework_Constraint_Not extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $constraint; + + /** + * @param PHPUnit_Framework_Constraint $constraint + */ + public function __construct($constraint) + { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + $constraint = new PHPUnit_Framework_Constraint_IsEqual($constraint); + } + + $this->constraint = $constraint; + } + + /** + * @param string $string + * @return string + */ + public static function negate($string) + { + return str_replace( + array( + 'contains ', + 'exists', + 'has ', + 'is ', + 'are ', + 'matches ', + 'starts with ', + 'ends with ', + 'reference ', + 'not not ' + ), + array( + 'does not contain ', + 'does not exist', + 'does not have ', + 'is not ', + 'are not ', + 'does not match ', + 'starts not with ', + 'ends not with ', + 'don\'t reference ', + 'not ' + ), + $string + ); + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $success = !$this->constraint->evaluate($other, $description, TRUE); + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + switch (get_class($this->constraint)) { + case 'PHPUnit_Framework_Constraint_And': + case 'PHPUnit_Framework_Constraint_Not': + case 'PHPUnit_Framework_Constraint_Or': { + return 'not( ' . $this->constraint->failureDescription($other) . ' )'; + } + break; + + default: { + return self::negate( + $this->constraint->failureDescription($other) + ); + } + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + switch (get_class($this->constraint)) { + case 'PHPUnit_Framework_Constraint_And': + case 'PHPUnit_Framework_Constraint_Not': + case 'PHPUnit_Framework_Constraint_Or': { + return 'not( ' . $this->constraint->toString() . ' )'; + } + break; + + default: { + return self::negate( + $this->constraint->toString() + ); + } + } + } + + /** + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.4.0 + */ + public function count() + { + return count($this->constraint); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php new file mode 100644 index 0000000..872c3b3 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php @@ -0,0 +1,77 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the object it is evaluated for has a given + * attribute. + * + * The attribute name is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_ObjectHasAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute +{ + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + $object = new ReflectionObject($other); + + return $object->hasProperty($this->attributeName); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Or.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Or.php new file mode 100644 index 0000000..6ee3842 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Or.php @@ -0,0 +1,157 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Logical OR. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_Or extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint[] + */ + protected $constraints = array(); + + /** + * @param PHPUnit_Framework_Constraint[] $constraints + */ + public function setConstraints(array $constraints) + { + $this->constraints = array(); + + foreach ($constraints as $key => $constraint) { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $constraint + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $success = FALSE; + $constraint = NULL; + + foreach ($this->constraints as $constraint) { + if ($constraint->evaluate($other, $description, TRUE)) { + $success = TRUE; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' or '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.4.0 + */ + public function count() + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + + return $count; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/PCREMatch.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/PCREMatch.php new file mode 100644 index 0000000..7f00e4a --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/PCREMatch.php @@ -0,0 +1,105 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the string it is evaluated for matches + * a regular expression. + * + * Checks a given value using the Perl Compatible Regular Expression extension + * in PHP. The pattern is matched by executing preg_match(). + * + * The pattern string passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_PCREMatch extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $pattern; + + /** + * @param string $pattern + */ + public function __construct($pattern) + { + $this->pattern = $pattern; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return preg_match($this->pattern, $other) > 0; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'matches PCRE pattern "%s"', + + $this->pattern + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/SameSize.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/SameSize.php new file mode 100644 index 0000000..2f4d8ab --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/SameSize.php @@ -0,0 +1,73 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Constraint_SameSize extends PHPUnit_Framework_Constraint_Count +{ + /** + * @var integer + */ + protected $expectedCount; + + /** + * @param integer $expected + */ + public function __construct($expected) + { + $this->expectedCount = $this->getCountOf($expected); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringContains.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringContains.php new file mode 100644 index 0000000..ab4eb41 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringContains.php @@ -0,0 +1,122 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the string it is evaluated for contains + * a given string. + * + * Uses strpos() to find the position of the string in the input, if not found + * the evaluaton fails. + * + * The sub-string is passed in the constructor. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_StringContains extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $string; + + /** + * @var boolean + */ + protected $ignoreCase; + + /** + * @param string $string + * @param boolean $ignoreCase + */ + public function __construct($string, $ignoreCase = FALSE) + { + $this->string = $string; + $this->ignoreCase = $ignoreCase; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + if ($this->ignoreCase) { + return stripos($other, $this->string) !== FALSE; + } else { + return strpos($other, $this->string) !== FALSE; + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + if ($this->ignoreCase) { + $string = strtolower($this->string); + } else { + $string = $this->string; + } + + return sprintf( + 'contains "%s"', + + $string + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringEndsWith.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringEndsWith.php new file mode 100644 index 0000000..cd145e6 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringEndsWith.php @@ -0,0 +1,96 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Constraint that asserts that the string it is evaluated for ends with a given + * suffix. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_Constraint_StringEndsWith extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $suffix; + + /** + * @param string $suffix + */ + public function __construct($suffix) + { + $this->suffix = $suffix; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return substr($other, 0 - strlen($this->suffix)) == $this->suffix; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'ends with "' . $this->suffix . '"'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringMatches.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringMatches.php new file mode 100644 index 0000000..9d21664 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringMatches.php @@ -0,0 +1,134 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */ + +/** + * ... + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.0 + */ +class PHPUnit_Framework_Constraint_StringMatches extends PHPUnit_Framework_Constraint_PCREMatch +{ + /** + * @var string + */ + protected $string; + + /** + * @param string $string + */ + public function __construct($string) + { + $this->pattern = $this->createPatternFromFormat( + preg_replace('/\r\n/', "\n", $string) + ); + $this->string = $string; + } + + protected function failureDescription($other) + { + return "format description matches text"; + } + + protected function additionalFailureDescription($other) + { + $from = preg_split('(\r\n|\r|\n)', $this->string); + $to = preg_split('(\r\n|\r|\n)', $other); + foreach ($from as $index => $line) { + if (isset($to[$index]) && $line !== $to[$index]) { + $line = $this->createPatternFromFormat($line); + if (preg_match($line, $to[$index]) > 0) { + $from[$index] = $to[$index]; + } + } + } + $this->string = join("\n", $from); + $other = join("\n", $to); + return PHPUnit_Util_Diff::diff($this->string, $other); + } + + protected function createPatternFromFormat($string) + { + $string = str_replace( + array( + '%e', + '%s', + '%S', + '%a', + '%A', + '%w', + '%i', + '%d', + '%x', + '%f', + '%c' + ), + array( + '\\' . DIRECTORY_SEPARATOR, + '[^\r\n]+', + '[^\r\n]*', + '.+', + '.*', + '\s*', + '[+-]?\d+', + '\d+', + '[0-9a-fA-F]+', + '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', + '.' + ), + preg_quote($string, '/') + ); + return '/^' . $string . '$/s'; + } + +} + diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringStartsWith.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringStartsWith.php new file mode 100644 index 0000000..6051a05 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/StringStartsWith.php @@ -0,0 +1,96 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Constraint that asserts that the string it is evaluated for begins with a + * given prefix. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_Constraint_StringStartsWith extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $prefix; + + /** + * @param string $prefix + */ + public function __construct($prefix) + { + $this->prefix = $prefix; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return strpos($other, $this->prefix) === 0; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'starts with "' . $this->prefix . '"'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContains.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContains.php new file mode 100644 index 0000000..46f1bed --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContains.php @@ -0,0 +1,152 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Constraint that asserts that the Traversable it is applied to contains + * a given value. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_TraversableContains extends PHPUnit_Framework_Constraint +{ + /** + * @var boolean + */ + protected $checkForObjectIdentity; + + /** + * @var mixed + */ + protected $value; + + /** + * @param boolean $value + * @param mixed $checkForObjectIdentity + * @throws PHPUnit_Framework_Exception + */ + public function __construct($value, $checkForObjectIdentity = TRUE) + { + if (!is_bool($checkForObjectIdentity)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean'); + } + + $this->checkForObjectIdentity = $checkForObjectIdentity; + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value); + } + + if (is_object($this->value)) { + foreach ($other as $element) { + if (($this->checkForObjectIdentity && + $element === $this->value) || + (!$this->checkForObjectIdentity && + $element == $this->value)) { + return TRUE; + } + } + } else { + foreach ($other as $element) { + if ($element == $this->value) { + return TRUE; + } + } + } + + return FALSE; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + if (is_string($this->value) && strpos($this->value, "\n") !== FALSE) { + return 'contains "' . $this->value . '"'; + } else { + return 'contains ' . PHPUnit_Util_Type::export($this->value); + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + 'an %s %s', + + is_array($other) ? 'array' : 'iterator', + $this->toString() + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php new file mode 100644 index 0000000..bab400c --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php @@ -0,0 +1,135 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.4 + */ + +/** + * Constraint that asserts that the Traversable it is applied to contains + * only values of a given type. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Framework_Constraint_TraversableContainsOnly extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $constraint; + + /** + * @var string + */ + protected $type; + + /** + * @param string $type + * @param boolean $isNativeType + */ + public function __construct($type, $isNativeType = TRUE) + { + if ($isNativeType) { + $this->constraint = new PHPUnit_Framework_Constraint_IsType($type); + } else { + $this->constraint = new PHPUnit_Framework_Constraint_IsInstanceOf( + $type + ); + } + + $this->type = $type; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $success = TRUE; + $constraint = NULL; + + foreach ($other as $item) { + if (!$this->constraint->evaluate($item, '', TRUE)) { + $success = FALSE; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'contains only values of type "' . $this->type . '"'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Xor.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Xor.php new file mode 100644 index 0000000..cc9c067 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Constraint/Xor.php @@ -0,0 +1,162 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Logical XOR. + * + * @package PHPUnit + * @subpackage Framework_Constraint + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_Xor extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint[] + */ + protected $constraints = array(); + + /** + * @param PHPUnit_Framework_Constraint[] $constraints + */ + public function setConstraints(array $constraints) + { + $this->constraints = array(); + + foreach ($constraints as $key => $constraint) { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $constraint + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to FALSE (the default), an exception is thrown + * in case of a failure. NULL is returned otherwise. + * + * If $returnResult is TRUE, the result of the evaluation is returned as + * a boolean value instead: TRUE in case of success, FALSE in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * @return mixed + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = FALSE) + { + $success = TRUE; + $lastResult = NULL; + $constraint = NULL; + + foreach ($this->constraints as $constraint) { + $result = $constraint->evaluate($other, $description, TRUE); + + if ($result === $lastResult) { + $success = FALSE; + break; + } + + $lastResult = $result; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' xor '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.4.0 + */ + public function count() + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + + return $count; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Error.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Error.php new file mode 100644 index 0000000..04f632a --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Error.php @@ -0,0 +1,75 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.2.0 + */ + +/** + * Wrapper for PHP errors. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.2.0 + */ +class PHPUnit_Framework_Error extends Exception +{ + /** + * Constructor. + * + * @param string $message + * @param integer $code + * @param string $file + * @param integer $line + * @param Exception $previous + */ + public function __construct($message, $code, $file, $line, Exception $previous = NULL) + { + parent::__construct($message, $code, $previous); + + $this->file = $file; + $this->line = $line; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Deprecated.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Deprecated.php new file mode 100644 index 0000000..4252668 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Deprecated.php @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Error + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * Wrapper for PHP deprecated errors. + * You can disable deprecated-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Deprecated::$enabled = FALSE; + * + * + * @package PHPUnit + * @subpackage Framework_Error + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Error_Deprecated extends PHPUnit_Framework_Error +{ + public static $enabled = TRUE; +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Notice.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Notice.php new file mode 100644 index 0000000..c640692 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Notice.php @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Error + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * Wrapper for PHP notices. + * You can disable notice-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Notice::$enabled = FALSE; + * + * + * @package PHPUnit + * @subpackage Framework_Error + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Error_Notice extends PHPUnit_Framework_Error +{ + public static $enabled = TRUE; +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Warning.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Warning.php new file mode 100644 index 0000000..d6064fc --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Error/Warning.php @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_Error + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * Wrapper for PHP warnings. + * You can disable notice-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Warning::$enabled = FALSE; + * + * + * @package PHPUnit + * @subpackage Framework_Error + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Error_Warning extends PHPUnit_Framework_Error +{ + public static $enabled = TRUE; +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Exception.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Exception.php new file mode 100644 index 0000000..2c0b398 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Exception.php @@ -0,0 +1,59 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Exception for PHPUnit runtime errors. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_Exception extends RuntimeException +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/ExpectationFailedException.php b/vendor/phpunit/phpunit/PHPUnit/Framework/ExpectationFailedException.php new file mode 100644 index 0000000..ba08851 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/ExpectationFailedException.php @@ -0,0 +1,82 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Exception for expectations which failed their check. + * + * The exception contains the error message and optionally a + * PHPUnit_Framework_ComparisonFailure which is used to + * generate diff output of the failed expectations. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_ExpectationFailedException extends PHPUnit_Framework_AssertionFailedError +{ + /** + * @var PHPUnit_Framework_ComparisonFailure + */ + protected $comparisonFailure; + + public function __construct($message, PHPUnit_Framework_ComparisonFailure $comparisonFailure = NULL, Exception $previous = NULL) + { + $this->comparisonFailure = $comparisonFailure; + + parent::__construct($message, 0, $previous); + } + + /** + * @return PHPUnit_Framework_ComparisonFailure + */ + public function getComparisonFailure() + { + return $this->comparisonFailure; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTest.php b/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTest.php new file mode 100644 index 0000000..12ac588 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTest.php @@ -0,0 +1,60 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A marker interface for marking any exception/error as result of an unit + * test as incomplete implementation or currently not implemented. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Framework_IncompleteTest +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTestError.php b/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTestError.php new file mode 100644 index 0000000..4ae6d5f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/IncompleteTestError.php @@ -0,0 +1,60 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of an incomplete test. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_IncompleteTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_IncompleteTest +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/OutputError.php b/vendor/phpunit/phpunit/PHPUnit/Framework/OutputError.php new file mode 100644 index 0000000..507363e --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/OutputError.php @@ -0,0 +1,60 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a test that printed output. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_OutputError extends PHPUnit_Framework_AssertionFailedError +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist b/vendor/phpunit/phpunit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist new file mode 100644 index 0000000..369e3ed --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist @@ -0,0 +1,51 @@ +setCodeCoverage(new PHP_CodeCoverage); + } + + $result->strictMode({strict}); + + $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(TRUE); + + ob_end_clean(); + ob_start(); + $test->run($result); + $output = ob_get_clean(); + + print serialize( + array( + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ) + ); + + ob_start(); +} + +{constants} +{included_files} +{globals} + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); +ob_end_clean(); diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/SelfDescribing.php b/vendor/phpunit/phpunit/PHPUnit/Framework/SelfDescribing.php new file mode 100644 index 0000000..3801405 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/SelfDescribing.php @@ -0,0 +1,65 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Interface for classes that can return a description of itself. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 3.0.0 + */ +interface PHPUnit_Framework_SelfDescribing +{ + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString(); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTest.php b/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTest.php new file mode 100644 index 0000000..5b3eae9 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTest.php @@ -0,0 +1,59 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * A marker interface for marking a unit test as being skipped. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 3.0.0 + */ +interface PHPUnit_Framework_SkippedTest +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestError.php b/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestError.php new file mode 100644 index 0000000..94c0022 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestError.php @@ -0,0 +1,60 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a skipped test. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_SkippedTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestSuiteError.php b/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestSuiteError.php new file mode 100644 index 0000000..95d4dde --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/SkippedTestSuiteError.php @@ -0,0 +1,60 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.0 + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a skipped test suite. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_SkippedTestSuiteError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest +{ +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/SyntheticError.php b/vendor/phpunit/phpunit/PHPUnit/Framework/SyntheticError.php new file mode 100644 index 0000000..0a6c618 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/SyntheticError.php @@ -0,0 +1,121 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.0 + */ + +/** + * Creates a synthetic failed assertion. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.0 + */ +class PHPUnit_Framework_SyntheticError extends PHPUnit_Framework_AssertionFailedError +{ + /** + * The synthetic file. + * + * @var string + */ + protected $syntheticFile = ''; + + /** + * The synthetic line number. + * + * @var integer + */ + protected $syntheticLine = 0; + + /** + * The synthetic trace. + * + * @var array + */ + protected $syntheticTrace = array(); + + /** + * Constructor. + * + * @param string $message + * @param integer $code + * @param string $file + * @param integer $line + * @param array $trace + */ + public function __construct($message, $code, $file, $line, $trace) + { + parent::__construct($message, $code); + + $this->syntheticFile = $file; + $this->syntheticLine = $line; + $this->syntheticTrace = $trace; + } + + /** + * @return string + */ + public function getSyntheticFile() + { + return $this->syntheticFile; + } + + /** + * @return integer + */ + public function getSyntheticLine() + { + return $this->syntheticLine; + } + + /** + * @return array + */ + public function getSyntheticTrace() + { + return $this->syntheticTrace; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Test.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Test.php new file mode 100644 index 0000000..c873748 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Test.php @@ -0,0 +1,66 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A Test can be run and collect its results. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Framework_Test extends Countable +{ + /** + * Runs a test and collects its result in a TestResult instance. + * + * @param PHPUnit_Framework_TestResult $result + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = NULL); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php b/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php new file mode 100644 index 0000000..a432a51 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php @@ -0,0 +1,1880 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A TestCase defines the fixture to run multiple tests. + * + * To define a TestCase + * + * 1) Implement a subclass of PHPUnit_Framework_TestCase. + * 2) Define instance variables that store the state of the fixture. + * 3) Initialize the fixture state by overriding setUp(). + * 4) Clean-up after a test by overriding tearDown(). + * + * Each test runs in its own fixture so there can be no side effects + * among test runs. + * + * Here is an example: + * + * + * value1 = 2; + * $this->value2 = 3; + * } + * } + * ?> + * + * + * For each test implement a method which interacts with the fixture. + * Verify the expected results with assertions specified by calling + * assert with a boolean. + * + * + * assertTrue($this->value1 + $this->value2 == 5); + * } + * ?> + * + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing +{ + /** + * Enable or disable the backup and restoration of the $GLOBALS array. + * Overwrite this attribute in a child class of TestCase. + * Setting this attribute in setUp() has no effect! + * + * @var boolean + */ + protected $backupGlobals = NULL; + + /** + * @var array + */ + protected $backupGlobalsBlacklist = array(); + + /** + * Enable or disable the backup and restoration of static attributes. + * Overwrite this attribute in a child class of TestCase. + * Setting this attribute in setUp() has no effect! + * + * @var boolean + */ + protected $backupStaticAttributes = NULL; + + /** + * @var array + */ + protected $backupStaticAttributesBlacklist = array(); + + /** + * Whether or not this test is to be run in a separate PHP process. + * + * @var boolean + */ + protected $runTestInSeparateProcess = NULL; + + /** + * Whether or not this test should preserve the global state when + * running in a separate PHP process. + * + * @var boolean + */ + protected $preserveGlobalState = TRUE; + + /** + * Whether or not this test is running in a separate PHP process. + * + * @var boolean + */ + private $inIsolation = FALSE; + + /** + * @var array + */ + private $data = array(); + + /** + * @var string + */ + private $dataName = ''; + + /** + * @var boolean + */ + private $useErrorHandler = NULL; + + /** + * @var boolean + */ + private $useOutputBuffering = NULL; + + /** + * The name of the expected Exception. + * + * @var mixed + */ + private $expectedException = NULL; + + /** + * The message of the expected Exception. + * + * @var string + */ + private $expectedExceptionMessage = ''; + + /** + * The code of the expected Exception. + * + * @var integer + */ + private $expectedExceptionCode; + + /** + * The required preconditions for a test. + * + * @var array + */ + private $required = array( + 'PHP' => NULL, + 'PHPUnit' => NULL, + 'functions' => array(), + 'extensions' => array() + ); + + /** + * The name of the test case. + * + * @var string + */ + private $name = NULL; + + /** + * @var array + */ + private $dependencies = array(); + + /** + * @var array + */ + private $dependencyInput = array(); + + /** + * @var array + */ + private $iniSettings = array(); + + /** + * @var array + */ + private $locale = array(); + + /** + * @var array + */ + private $mockObjects = array(); + + /** + * @var integer + */ + private $status; + + /** + * @var string + */ + private $statusMessage = ''; + + /** + * @var integer + */ + private $numAssertions = 0; + + /** + * @var PHPUnit_Framework_TestResult + */ + private $result; + + /** + * @var mixed + */ + private $testResult; + + /** + * @var string + */ + private $output = ''; + + /** + * @var string + */ + private $outputExpectedRegex = NULL; + + /** + * @var string + */ + private $outputExpectedString = NULL; + + /** + * @var bool + */ + private $hasPerformedExpectationsOnOutput = FALSE; + + /** + * @var mixed + */ + private $outputCallback = FALSE; + + /** + * @var boolean + */ + private $outputBufferingActive = FALSE; + + /** + * Constructs a test case with the given name. + * + * @param string $name + * @param array $data + * @param string $dataName + */ + public function __construct($name = NULL, array $data = array(), $dataName = '') + { + if ($name !== NULL) { + $this->setName($name); + } + + $this->data = $data; + $this->dataName = $dataName; + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + $class = new ReflectionClass($this); + + $buffer = sprintf( + '%s::%s', + + $class->name, + $this->getName(FALSE) + ); + + return $buffer . $this->getDataSetAsString(); + } + + /** + * Counts the number of test cases executed by run(TestResult result). + * + * @return integer + */ + public function count() + { + return 1; + } + + /** + * Returns the annotations for this test. + * + * @return array + * @since Method available since Release 3.4.0 + */ + public function getAnnotations() + { + return PHPUnit_Util_Test::parseTestMethodAnnotations( + get_class($this), $this->name + ); + } + + /** + * Gets the name of a TestCase. + * + * @param boolean $withDataSet + * @return string + */ + public function getName($withDataSet = TRUE) + { + if ($withDataSet) { + return $this->name . $this->getDataSetAsString(FALSE); + } else { + return $this->name; + } + } + + /** + * Returns the size of the test. + * + * @return integer + * @since Method available since Release 3.6.0 + */ + public function getSize() + { + return PHPUnit_Util_Test::getSize( + get_class($this), $this->getName(FALSE) + ); + } + + /** + * @return string + * @since Method available since Release 3.6.0 + */ + public function getActualOutput() + { + if (!$this->outputBufferingActive) { + return $this->output; + } else { + return ob_get_contents(); + } + } + + /** + * @return string + * @since Method available since Release 3.6.0 + */ + public function hasOutput() + { + if (empty($this->output)) { + return FALSE; + } + + if ($this->outputExpectedString !== NULL || + $this->outputExpectedRegex !== NULL || + $this->hasPerformedExpectationsOnOutput) { + return FALSE; + } + + return TRUE; + } + + /** + * @param string $expectedRegex + * @since Method available since Release 3.6.0 + */ + public function expectOutputRegex($expectedRegex) + { + if ($this->outputExpectedString !== NULL) { + throw new PHPUnit_Framework_Exception; + } + + if (is_string($expectedRegex) || is_null($expectedRegex)) { + $this->outputExpectedRegex = $expectedRegex; + } + } + + /** + * @param string $expectedString + * @since Method available since Release 3.6.0 + */ + public function expectOutputString($expectedString) + { + if ($this->outputExpectedRegex !== NULL) { + throw new PHPUnit_Framework_Exception; + } + + if (is_string($expectedString) || is_null($expectedString)) { + $this->outputExpectedString = $expectedString; + } + } + + /** + * @return bool + * @since Method available since Release 3.6.5 + */ + public function hasPerformedExpectationsOnOutput() + { + return $this->hasPerformedExpectationsOnOutput; + } + + /** + * @return string + * @since Method available since Release 3.2.0 + */ + public function getExpectedException() + { + return $this->expectedException; + } + + /** + * @param mixed $exceptionName + * @param string $exceptionMessage + * @param integer $exceptionCode + * @since Method available since Release 3.2.0 + */ + public function setExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = NULL) + { + $this->expectedException = $exceptionName; + $this->expectedExceptionMessage = $exceptionMessage; + $this->expectedExceptionCode = $exceptionCode; + } + + /** + * @since Method available since Release 3.4.0 + */ + protected function setExpectedExceptionFromAnnotation() + { + try { + $expectedException = PHPUnit_Util_Test::getExpectedException( + get_class($this), $this->name + ); + + if ($expectedException !== FALSE) { + $this->setExpectedException( + $expectedException['class'], + $expectedException['message'], + $expectedException['code'] + ); + } + } + + catch (ReflectionException $e) { + } + } + + /** + * @param boolean $useErrorHandler + * @since Method available since Release 3.4.0 + */ + public function setUseErrorHandler($useErrorHandler) + { + $this->useErrorHandler = $useErrorHandler; + } + + /** + * @since Method available since Release 3.4.0 + */ + protected function setUseErrorHandlerFromAnnotation() + { + try { + $useErrorHandler = PHPUnit_Util_Test::getErrorHandlerSettings( + get_class($this), $this->name + ); + + if ($useErrorHandler !== NULL) { + $this->setUseErrorHandler($useErrorHandler); + } + } + + catch (ReflectionException $e) { + } + } + + /** + * @param boolean $useOutputBuffering + * @since Method available since Release 3.4.0 + */ + public function setUseOutputBuffering($useOutputBuffering) + { + $this->useOutputBuffering = $useOutputBuffering; + } + + /** + * @since Method available since Release 3.4.0 + */ + protected function setUseOutputBufferingFromAnnotation() + { + try { + $useOutputBuffering = PHPUnit_Util_Test::getOutputBufferingSettings( + get_class($this), $this->name + ); + + if ($useOutputBuffering !== NULL) { + $this->setUseOutputBuffering($useOutputBuffering); + } + } + + catch (ReflectionException $e) { + } + } + + /** + * @since Method available since Release 3.6.0 + */ + protected function setRequirementsFromAnnotation() + { + try { + $requirements = PHPUnit_Util_Test::getRequirements( + get_class($this), $this->name + ); + + if (isset($requirements['PHP'])) { + $this->required['PHP'] = $requirements['PHP']; + } + + if (isset($requirements['PHPUnit'])) { + $this->required['PHPUnit'] = $requirements['PHPUnit']; + } + + if (isset($requirements['extensions'])) { + $this->required['extensions'] = $requirements['extensions']; + } + + if (isset($requirements['functions'])) { + $this->required['functions'] = $requirements['functions']; + } + } + + catch (ReflectionException $e) { + } + } + + /** + * @since Method available since Release 3.6.0 + */ + protected function checkRequirements() + { + $this->setRequirementsFromAnnotation(); + + $missingRequirements = array(); + + if ($this->required['PHP'] && + version_compare(PHP_VERSION, $this->required['PHP'], '<')) { + $missingRequirements[] = sprintf( + 'PHP %s (or later) is required.', + $this->required['PHP'] + ); + } + + $phpunitVersion = PHPUnit_Runner_Version::id(); + if ($this->required['PHPUnit'] && + version_compare($phpunitVersion, $this->required['PHPUnit'], '<')) { + $missingRequirements[] = sprintf( + 'PHPUnit %s (or later) is required.', + $this->required['PHPUnit'] + ); + } + + foreach ($this->required['functions'] as $requiredFunction) { + if (!function_exists($requiredFunction)) { + $missingRequirements[] = sprintf( + 'Function %s is required.', + $requiredFunction + ); + } + } + + foreach ($this->required['extensions'] as $requiredExtension) { + if (!extension_loaded($requiredExtension)) { + $missingRequirements[] = sprintf( + 'Extension %s is required.', + $requiredExtension + ); + } + } + + if ($missingRequirements) { + $this->markTestSkipped( + implode( + PHP_EOL, + $missingRequirements + ) + ); + } + } + + /** + * Returns the status of this test. + * + * @return integer + * @since Method available since Release 3.1.0 + */ + public function getStatus() + { + return $this->status; + } + + /** + * Returns the status message of this test. + * + * @return string + * @since Method available since Release 3.3.0 + */ + public function getStatusMessage() + { + return $this->statusMessage; + } + + /** + * Returns whether or not this test has failed. + * + * @return boolean + * @since Method available since Release 3.0.0 + */ + public function hasFailed() + { + $status = $this->getStatus(); + + return $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE || + $status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; + } + + /** + * Runs the test case and collects the results in a TestResult object. + * If no TestResult object is passed a new one will be created. + * + * @param PHPUnit_Framework_TestResult $result + * @return PHPUnit_Framework_TestResult + * @throws PHPUnit_Framework_Exception + */ + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + if ($result === NULL) { + $result = $this->createResult(); + } + + if (!$this instanceof PHPUnit_Framework_Warning) { + $this->setTestResultObject($result); + $this->setUseErrorHandlerFromAnnotation(); + $this->setUseOutputBufferingFromAnnotation(); + } + + if ($this->useErrorHandler !== NULL) { + $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions(); + $result->convertErrorsToExceptions($this->useErrorHandler); + } + + if (!$this->handleDependencies()) { + return; + } + + if ($this->runTestInSeparateProcess === TRUE && + $this->inIsolation !== TRUE && + !$this instanceof PHPUnit_Extensions_SeleniumTestCase && + !$this instanceof PHPUnit_Extensions_PhptTestCase) { + $class = new ReflectionClass($this); + + $template = new Text_Template( + sprintf( + '%s%sProcess%sTestCaseMethod.tpl', + + __DIR__, + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR + ) + ); + + if ($this->preserveGlobalState) { + $constants = PHPUnit_Util_GlobalState::getConstantsAsString(); + $globals = PHPUnit_Util_GlobalState::getGlobalsAsString(); + $includedFiles = PHPUnit_Util_GlobalState::getIncludedFilesAsString(); + } else { + $constants = ''; + $globals = ''; + $includedFiles = ''; + } + + if ($result->getCollectCodeCoverageInformation()) { + $coverage = 'TRUE'; + } else { + $coverage = 'FALSE'; + } + + if ($result->isStrict()) { + $strict = 'TRUE'; + } else { + $strict = 'FALSE'; + } + + $data = var_export(serialize($this->data), TRUE); + $dependencyInput = var_export(serialize($this->dependencyInput), TRUE); + $includePath = var_export(get_include_path(), TRUE); + // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC + // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences + $data = "'." . $data . ".'"; + $dependencyInput = "'." . $dependencyInput . ".'"; + $includePath = "'." . $includePath . ".'"; + + $template->setVar( + array( + 'filename' => $class->getFileName(), + 'className' => $class->getName(), + 'methodName' => $this->name, + 'collectCodeCoverageInformation' => $coverage, + 'data' => $data, + 'dataName' => $this->dataName, + 'dependencyInput' => $dependencyInput, + 'constants' => $constants, + 'globals' => $globals, + 'include_path' => $includePath, + 'included_files' => $includedFiles, + 'strict' => $strict + ) + ); + + $this->prepareTemplate($template); + + $php = PHPUnit_Util_PHP::factory(); + $php->runJob($template->render(), $this, $result); + } else { + $result->run($this); + } + + if ($this->useErrorHandler !== NULL) { + $result->convertErrorsToExceptions($oldErrorHandlerSetting); + } + + $this->result = NULL; + + return $result; + } + + /** + * Runs the bare test sequence. + */ + public function runBare() + { + $this->numAssertions = 0; + + // Backup the $GLOBALS array and static attributes. + if ($this->runTestInSeparateProcess !== TRUE && + $this->inIsolation !== TRUE) { + if ($this->backupGlobals === NULL || + $this->backupGlobals === TRUE) { + PHPUnit_Util_GlobalState::backupGlobals( + $this->backupGlobalsBlacklist + ); + } + + if ($this->backupStaticAttributes === TRUE) { + PHPUnit_Util_GlobalState::backupStaticAttributes( + $this->backupStaticAttributesBlacklist + ); + } + } + + // Start output buffering. + ob_start(); + $this->outputBufferingActive = TRUE; + + // Clean up stat cache. + clearstatcache(); + + // Backup the cwd + $currentWorkingDirectory = getcwd(); + + try { + if ($this->inIsolation) { + $this->setUpBeforeClass(); + } + + $this->setExpectedExceptionFromAnnotation(); + $this->setUp(); + $this->checkRequirements(); + $this->assertPreConditions(); + $this->testResult = $this->runTest(); + $this->verifyMockObjects(); + $this->assertPostConditions(); + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED; + } + + catch (PHPUnit_Framework_IncompleteTest $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE; + $this->statusMessage = $e->getMessage(); + } + + catch (PHPUnit_Framework_SkippedTest $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED; + $this->statusMessage = $e->getMessage(); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } + + catch (Exception $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $e->getMessage(); + } + + // Clean up the mock objects. + $this->mockObjects = array(); + + // Tear down the fixture. An exception raised in tearDown() will be + // caught and passed on when no exception was raised before. + try { + $this->tearDown(); + + if ($this->inIsolation) { + $this->tearDownAfterClass(); + } + } + + catch (Exception $_e) { + if (!isset($e)) { + $e = $_e; + } + } + + // Stop output buffering. + if ($this->outputCallback === FALSE) { + $this->output = ob_get_contents(); + } else { + $this->output = call_user_func_array( + $this->outputCallback, array(ob_get_contents()) + ); + } + + ob_end_clean(); + $this->outputBufferingActive = FALSE; + + // Clean up stat cache. + clearstatcache(); + + // Restore the cwd if it was changed by the test + if ($currentWorkingDirectory != getcwd()) { + chdir($currentWorkingDirectory); + } + + // Restore the $GLOBALS array and static attributes. + if ($this->runTestInSeparateProcess !== TRUE && + $this->inIsolation !== TRUE) { + if ($this->backupGlobals === NULL || + $this->backupGlobals === TRUE) { + PHPUnit_Util_GlobalState::restoreGlobals( + $this->backupGlobalsBlacklist + ); + } + + if ($this->backupStaticAttributes === TRUE) { + PHPUnit_Util_GlobalState::restoreStaticAttributes(); + } + } + + // Clean up INI settings. + foreach ($this->iniSettings as $varName => $oldValue) { + ini_set($varName, $oldValue); + } + + $this->iniSettings = array(); + + // Clean up locale settings. + foreach ($this->locale as $category => $locale) { + setlocale($category, $locale); + } + + // Perform assertion on output. + if (!isset($e)) { + try { + if ($this->outputExpectedRegex !== NULL) { + $this->hasPerformedExpectationsOnOutput = TRUE; + $this->assertRegExp($this->outputExpectedRegex, $this->output); + $this->outputExpectedRegex = NULL; + } + + else if ($this->outputExpectedString !== NULL) { + $this->hasPerformedExpectationsOnOutput = TRUE; + $this->assertEquals($this->outputExpectedString, $this->output); + $this->outputExpectedString = NULL; + } + } + + catch (Exception $_e) { + $e = $_e; + } + } + + // Workaround for missing "finally". + if (isset($e)) { + $this->onNotSuccessfulTest($e); + } + } + + /** + * Override to run the test and assert its state. + * + * @return mixed + * @throws PHPUnit_Framework_Exception + */ + protected function runTest() + { + if ($this->name === NULL) { + throw new PHPUnit_Framework_Exception( + 'PHPUnit_Framework_TestCase::$name must not be NULL.' + ); + } + + try { + $class = new ReflectionClass($this); + $method = $class->getMethod($this->name); + } + + catch (ReflectionException $e) { + $this->fail($e->getMessage()); + } + + try { + $testResult = $method->invokeArgs( + $this, array_merge($this->data, $this->dependencyInput) + ); + } + + catch (Exception $e) { + $checkException = FALSE; + + if (is_string($this->expectedException)) { + $checkException = TRUE; + + if ($e instanceof PHPUnit_Framework_Exception) { + $checkException = FALSE; + } + + $reflector = new ReflectionClass($this->expectedException); + + if ($this->expectedException == 'PHPUnit_Framework_Exception' || + $reflector->isSubclassOf('PHPUnit_Framework_Exception')) { + $checkException = TRUE; + } + } + + if ($checkException) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_Exception( + $this->expectedException + ) + ); + + if (is_string($this->expectedExceptionMessage) && + !empty($this->expectedExceptionMessage)) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionMessage( + $this->expectedExceptionMessage + ) + ); + } + + if ($this->expectedExceptionCode !== NULL) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionCode( + $this->expectedExceptionCode + ) + ); + } + + return; + } else { + throw $e; + } + } + + if ($this->expectedException !== NULL) { + $this->assertThat( + NULL, + new PHPUnit_Framework_Constraint_Exception( + $this->expectedException + ) + ); + } + + return $testResult; + } + + /** + * Verifies the mock object expectations. + * + * @since Method available since Release 3.5.0 + */ + protected function verifyMockObjects() + { + foreach ($this->mockObjects as $mockObject) { + if ($mockObject->__phpunit_hasMatchers()) { + $this->numAssertions++; + } + + $mockObject->__phpunit_verify(); + $mockObject->__phpunit_cleanup(); + } + } + + /** + * Sets the name of a TestCase. + * + * @param string + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Sets the dependencies of a TestCase. + * + * @param array $dependencies + * @since Method available since Release 3.4.0 + */ + public function setDependencies(array $dependencies) + { + $this->dependencies = $dependencies; + } + + /** + * Sets + * + * @param array $dependencyInput + * @since Method available since Release 3.4.0 + */ + public function setDependencyInput(array $dependencyInput) + { + $this->dependencyInput = $dependencyInput; + } + + /** + * Calling this method in setUp() has no effect! + * + * @param boolean $backupGlobals + * @since Method available since Release 3.3.0 + */ + public function setBackupGlobals($backupGlobals) + { + if (is_null($this->backupGlobals) && is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; + } + } + + /** + * Calling this method in setUp() has no effect! + * + * @param boolean $backupStaticAttributes + * @since Method available since Release 3.4.0 + */ + public function setBackupStaticAttributes($backupStaticAttributes) + { + if (is_null($this->backupStaticAttributes) && + is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; + } + } + + /** + * @param boolean $runTestInSeparateProcess + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.4.0 + */ + public function setRunTestInSeparateProcess($runTestInSeparateProcess) + { + if (is_bool($runTestInSeparateProcess)) { + if ($this->runTestInSeparateProcess === NULL) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * @param boolean $preserveGlobalState + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.4.0 + */ + public function setPreserveGlobalState($preserveGlobalState) + { + if (is_bool($preserveGlobalState)) { + $this->preserveGlobalState = $preserveGlobalState; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * @param boolean $inIsolation + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.4.0 + */ + public function setInIsolation($inIsolation) + { + if (is_bool($inIsolation)) { + $this->inIsolation = $inIsolation; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * @return mixed + * @since Method available since Release 3.4.0 + */ + public function getResult() + { + return $this->testResult; + } + + /** + * @param mixed $result + * @since Method available since Release 3.4.0 + */ + public function setResult($result) + { + $this->testResult = $result; + } + + /** + * @param callable $callback + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.6.0 + */ + public function setOutputCallback($callback) + { + if (!is_callable($callback)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'callback'); + } + + $this->outputCallback = $callback; + } + + /** + * @return PHPUnit_Framework_TestResult + * @since Method available since Release 3.5.7 + */ + public function getTestResultObject() + { + return $this->result; + } + + /** + * @param PHPUnit_Framework_TestResult $result + * @since Method available since Release 3.6.0 + */ + public function setTestResultObject(PHPUnit_Framework_TestResult $result) + { + $this->result = $result; + } + + /** + * This method is a wrapper for the ini_set() function that automatically + * resets the modified php.ini setting to its original value after the + * test is run. + * + * @param string $varName + * @param string $newValue + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.0.0 + */ + protected function iniSet($varName, $newValue) + { + if (!is_string($varName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $currentValue = ini_set($varName, $newValue); + + if ($currentValue !== FALSE) { + $this->iniSettings[$varName] = $currentValue; + } else { + throw new PHPUnit_Framework_Exception( + sprintf( + 'INI setting "%s" could not be set to "%s".', + $varName, + $newValue + ) + ); + } + } + + /** + * This method is a wrapper for the setlocale() function that automatically + * resets the locale to its original value after the test is run. + * + * @param integer $category + * @param string $locale + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.1.0 + */ + protected function setLocale() + { + $args = func_get_args(); + + if (count($args) < 2) { + throw new PHPUnit_Framework_Exception; + } + + $category = $args[0]; + $locale = $args[1]; + + $categories = array( + LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME + ); + + if (defined('LC_MESSAGES')) { + $categories[] = LC_MESSAGES; + } + + if (!in_array($category, $categories)) { + throw new PHPUnit_Framework_Exception; + } + + if (!is_array($locale) && !is_string($locale)) { + throw new PHPUnit_Framework_Exception; + } + + $this->locale[$category] = setlocale($category, NULL); + + $result = call_user_func_array( 'setlocale', $args ); + + if ($result === FALSE) { + throw new PHPUnit_Framework_Exception( + 'The locale functionality is not implemented on your platform, ' . + 'the specified locale does not exist or the category name is ' . + 'invalid.' + ); + } + } + + /** + * Returns a mock object for the specified class. + * + * @param string $originalClassName + * @param array $methods + * @param array $arguments + * @param string $mockClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param boolean $cloneArguments + * @return PHPUnit_Framework_MockObject_MockObject + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.0.0 + */ + public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = FALSE) + { + $mockObject = PHPUnit_Framework_MockObject_Generator::getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + + $this->mockObjects[] = $mockObject; + + return $mockObject; + } + + /** + * Returns a builder object to create mock objects using a fluent interface. + * + * @param string $className + * @return PHPUnit_Framework_MockObject_MockBuilder + * @since Method available since Release 3.5.0 + */ + public function getMockBuilder($className) + { + return new PHPUnit_Framework_MockObject_MockBuilder( + $this, $className + ); + } + + /** + * Mocks the specified class and returns the name of the mocked class. + * + * @param string $originalClassName + * @param array $methods + * @param array $arguments + * @param string $mockClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param boolean $cloneArguments + * @return string + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.5.0 + */ + protected function getMockClass($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = FALSE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = FALSE) + { + $mock = $this->getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + + return get_class($mock); + } + + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods to mock can be specified with + * the last parameter + * + * @param string $originalClassName + * @param array $arguments + * @param string $mockClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param array $mockedMethods + * @param boolean $cloneArguments + * @return PHPUnit_Framework_MockObject_MockObject + * @since Method available since Release 3.4.0 + * @throws PHPUnit_Framework_Exception + */ + public function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $mockedMethods = array(), $cloneArguments = FALSE) + { + $mockObject = PHPUnit_Framework_MockObject_Generator::getMockForAbstractClass( + $originalClassName, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $mockedMethods, + $cloneArguments + ); + + $this->mockObjects[] = $mockObject; + + return $mockObject; + } + + /** + * Returns a mock object based on the given WSDL file. + * + * @param string $wsdlFile + * @param string $originalClassName + * @param string $mockClassName + * @param array $methods + * @param boolean $callOriginalConstructor + * @return PHPUnit_Framework_MockObject_MockObject + * @since Method available since Release 3.4.0 + */ + protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = array(), $callOriginalConstructor = TRUE) + { + if ($originalClassName === '') { + $originalClassName = str_replace( + '.wsdl', '', basename($wsdlFile) + ); + } + + if (!class_exists($originalClassName)) { + eval( + PHPUnit_Framework_MockObject_Generator::generateClassFromWsdl( + $wsdlFile, $originalClassName, $methods + ) + ); + } + + return $this->getMock( + $originalClassName, + $methods, + array('', array()), + $mockClassName, + $callOriginalConstructor, + FALSE, + FALSE + ); + } + + /** + * Returns an object for the specified trait. + * + * @param string $traitName + * @param array $arguments + * @param string $traitClassName + * @param boolean $callOriginalConstructor + * @param boolean $callOriginalClone + * @param boolean $callAutoload + * @param boolean $cloneArguments + * @return object + * @since Method available since Release 3.6.0 + * @throws PHPUnit_Framework_Exception + */ + protected function getObjectForTrait($traitName, array $arguments = array(), $traitClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = FALSE) + { + return PHPUnit_Framework_MockObject_Generator::getObjectForTrait( + $traitName, + $arguments, + $traitClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + } + + /** + * Adds a value to the assertion counter. + * + * @param integer $count + * @since Method available since Release 3.3.3 + */ + public function addToAssertionCount($count) + { + $this->numAssertions += $count; + } + + /** + * Returns the number of assertions performed by this test. + * + * @return integer + * @since Method available since Release 3.3.0 + */ + public function getNumAssertions() + { + return $this->numAssertions; + } + + /** + * Returns a matcher that matches when the method it is evaluated for + * is executed zero or more times. + * + * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount + * @since Method available since Release 3.0.0 + */ + public static function any() + { + return new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount; + } + + /** + * Returns a matcher that matches when the method it is evaluated for + * is never executed. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * @since Method available since Release 3.0.0 + */ + public static function never() + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(0); + } + + /** + * Returns a matcher that matches when the method it is evaluated for + * is executed at least once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce + * @since Method available since Release 3.0.0 + */ + public static function atLeastOnce() + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce; + } + + /** + * Returns a matcher that matches when the method it is evaluated for + * is executed exactly once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * @since Method available since Release 3.0.0 + */ + public static function once() + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(1); + } + + /** + * Returns a matcher that matches when the method it is evaluated for + * is executed exactly $count times. + * + * @param integer $count + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * @since Method available since Release 3.0.0 + */ + public static function exactly($count) + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedCount($count); + } + + /** + * Returns a matcher that matches when the method it is evaluated for + * is invoked at the given $index. + * + * @param integer $index + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex + * @since Method available since Release 3.0.0 + */ + public static function at($index) + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex($index); + } + + /** + * + * + * @param mixed $value + * @return PHPUnit_Framework_MockObject_Stub_Return + * @since Method available since Release 3.0.0 + */ + public static function returnValue($value) + { + return new PHPUnit_Framework_MockObject_Stub_Return($value); + } + + /** + * + * + * @param array $valueMap + * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap + * @since Method available since Release 3.6.0 + */ + public static function returnValueMap(array $valueMap) + { + return new PHPUnit_Framework_MockObject_Stub_ReturnValueMap($valueMap); + } + + /** + * + * + * @param integer $argumentIndex + * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument + * @since Method available since Release 3.3.0 + */ + public static function returnArgument($argumentIndex) + { + return new PHPUnit_Framework_MockObject_Stub_ReturnArgument( + $argumentIndex + ); + } + + /** + * + * + * @param mixed $callback + * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback + * @since Method available since Release 3.3.0 + */ + public static function returnCallback($callback) + { + return new PHPUnit_Framework_MockObject_Stub_ReturnCallback($callback); + } + + /** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf + * @since Method available since Release 3.6.0 + */ + public static function returnSelf() + { + return new PHPUnit_Framework_MockObject_Stub_ReturnSelf(); + } + + /** + * + * + * @param Exception $exception + * @return PHPUnit_Framework_MockObject_Stub_Exception + * @since Method available since Release 3.1.0 + */ + public static function throwException(Exception $exception) + { + return new PHPUnit_Framework_MockObject_Stub_Exception($exception); + } + + /** + * + * + * @param mixed $value, ... + * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls + * @since Method available since Release 3.0.0 + */ + public static function onConsecutiveCalls() + { + $args = func_get_args(); + + return new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($args); + } + + /** + * @param mixed $data + * @return string + * @since Method available since Release 3.2.1 + */ + protected function dataToString($data) + { + $result = array(); + + // There seems to be no other way to check arrays for recursion + // http://www.php.net/manual/en/language.types.array.php#73936 + preg_match_all('/\n \[(\w+)\] => Array\s+\*RECURSION\*/', print_r($data, TRUE), $matches); + $recursiveKeys = array_unique($matches[1]); + + // Convert to valid array keys + // Numeric integer strings are automatically converted to integers + // by PHP + foreach ($recursiveKeys as $key => $recursiveKey) { + if ((string)(integer)$recursiveKey === $recursiveKey) { + $recursiveKeys[$key] = (integer)$recursiveKey; + } + } + + foreach ($data as $key => $_data) { + if (in_array($key, $recursiveKeys, TRUE)) { + $result[] = '*RECURSION*'; + } + + else if (is_array($_data)) { + $result[] = 'array(' . $this->dataToString($_data) . ')'; + } + + else if (is_object($_data)) { + $object = new ReflectionObject($_data); + + if ($object->hasMethod('__toString')) { + $result[] = (string)$_data; + } else { + $result[] = get_class($_data); + } + } + + else if (is_resource($_data)) { + $result[] = ''; + } + + else { + $result[] = var_export($_data, TRUE); + } + } + + return join(', ', $result); + } + + /** + * Gets the data set description of a TestCase. + * + * @param boolean $includeData + * @return string + * @since Method available since Release 3.3.0 + */ + protected function getDataSetAsString($includeData = TRUE) + { + $buffer = ''; + + if (!empty($this->data)) { + if (is_int($this->dataName)) { + $buffer .= sprintf(' with data set #%d', $this->dataName); + } else { + $buffer .= sprintf(' with data set "%s"', $this->dataName); + } + + if ($includeData) { + $buffer .= sprintf(' (%s)', $this->dataToString($this->data)); + } + } + + return $buffer; + } + + /** + * Creates a default TestResult object. + * + * @return PHPUnit_Framework_TestResult + */ + protected function createResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * @since Method available since Release 3.5.4 + */ + protected function handleDependencies() + { + if (!empty($this->dependencies) && !$this->inIsolation) { + $className = get_class($this); + $passed = $this->result->passed(); + $passedKeys = array_keys($passed); + $numKeys = count($passedKeys); + + for ($i = 0; $i < $numKeys; $i++) { + $pos = strpos($passedKeys[$i], ' with data set'); + + if ($pos !== FALSE) { + $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); + } + } + + $passedKeys = array_flip(array_unique($passedKeys)); + + foreach ($this->dependencies as $dependency) { + if (strpos($dependency, '::') === FALSE) { + $dependency = $className . '::' . $dependency; + } + + if (!isset($passedKeys[$dependency])) { + $this->result->addError( + $this, + new PHPUnit_Framework_SkippedTestError( + sprintf( + 'This test depends on "%s" to pass.', $dependency + ) + ), + 0 + ); + + return FALSE; + } + + if (isset($passed[$dependency])) { + if ($passed[$dependency]['size'] > $this->getSize()) { + $this->result->addError( + $this, + new PHPUnit_Framework_SkippedTestError( + 'This test depends on a test that is larger than itself.' + ), + 0 + ); + + return FALSE; + } + + $this->dependencyInput[] = $passed[$dependency]['result']; + } else { + $this->dependencyInput[] = NULL; + } + } + } + + return TRUE; + } + + /** + * This method is called before the first test of this test class is run. + * + * @since Method available since Release 3.4.0 + */ + public static function setUpBeforeClass() + { + } + + /** + * Sets up the fixture, for example, open a network connection. + * This method is called before a test is executed. + * + */ + protected function setUp() + { + } + + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called before the execution of a test starts + * and after setUp() is called. + * + * @since Method available since Release 3.2.8 + */ + protected function assertPreConditions() + { + } + + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called before the execution of a test ends + * and before tearDown() is called. + * + * @since Method available since Release 3.2.8 + */ + protected function assertPostConditions() + { + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * This method is called after the last test of this test class is run. + * + * @since Method available since Release 3.4.0 + */ + public static function tearDownAfterClass() + { + } + + /** + * This method is called when a test method did not execute successfully. + * + * @param Exception $e + * @since Method available since Release 3.4.0 + */ + protected function onNotSuccessfulTest(Exception $e) + { + throw $e; + } + + /** + * Performs custom preparations on the process isolation template. + * + * @param Text_Template $template + * @since Method available since Release 3.4.0 + */ + protected function prepareTemplate(Text_Template $template) + { + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/TestFailure.php b/vendor/phpunit/phpunit/PHPUnit/Framework/TestFailure.php new file mode 100644 index 0000000..5bde6af --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/TestFailure.php @@ -0,0 +1,179 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A TestFailure collects a failed test together with the caught exception. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_TestFailure +{ + /** + * @var PHPUnit_Framework_Test + */ + protected $failedTest; + + /** + * @var Exception + */ + protected $thrownException; + + /** + * Constructs a TestFailure with the given test and exception. + * + * @param PHPUnit_Framework_Test $failedTest + * @param Exception $thrownException + */ + public function __construct(PHPUnit_Framework_Test $failedTest, Exception $thrownException) + { + $this->failedTest = $failedTest; + $this->thrownException = $thrownException; + } + + /** + * Returns a short description of the failure. + * + * @return string + */ + public function toString() + { + return sprintf( + '%s: %s', + + $this->failedTest, + $this->thrownException->getMessage() + ); + } + + /** + * Returns a description for the thrown exception. + * + * @return string + * @since Method available since Release 3.4.0 + */ + public function getExceptionAsString() + { + return self::exceptionToString($this->thrownException); + } + + /** + * Returns a description for an exception. + * + * @param Exception $e + * @return string + * @since Method available since Release 3.2.0 + */ + public static function exceptionToString(Exception $e) + { + if ($e instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $e->toString(); + + if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) { + $buffer = $buffer . "\n" . $e->getComparisonFailure()->getDiff(); + } + + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; + } + } + + else if ($e instanceof PHPUnit_Framework_Error) { + $buffer = $e->getMessage() . "\n"; + } + + else { + $buffer = get_class($e) . ': ' . $e->getMessage() . "\n"; + } + + return $buffer; + } + + /** + * Gets the failed test. + * + * @return Test + */ + public function failedTest() + { + return $this->failedTest; + } + + /** + * Gets the thrown exception. + * + * @return Exception + */ + public function thrownException() + { + return $this->thrownException; + } + + /** + * Returns the exception's message. + * + * @return string + */ + public function exceptionMessage() + { + return $this->thrownException()->getMessage(); + } + + /** + * Returns TRUE if the thrown exception + * is of type AssertionFailedError. + * + * @return boolean + */ + public function isFailure() + { + return ($this->thrownException() instanceof PHPUnit_Framework_AssertionFailedError); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/TestListener.php b/vendor/phpunit/phpunit/PHPUnit/Framework/TestListener.php new file mode 100644 index 0000000..e25fef7 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/TestListener.php @@ -0,0 +1,126 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A Listener for test progress. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Framework_TestListener +{ + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time); + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite); + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite); + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test); + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php b/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php new file mode 100644 index 0000000..2df29ab --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php @@ -0,0 +1,956 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A TestResult collects the results of executing a test case. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_TestResult implements Countable +{ + /** + * @var boolean + */ + protected static $xdebugLoaded = NULL; + + /** + * @var boolean + */ + protected static $useXdebug = NULL; + + /** + * @var array + */ + protected $passed = array(); + + /** + * @var array + */ + protected $errors = array(); + + /** + * @var array + */ + protected $deprecatedFeatures = array(); + + /** + * @var array + */ + protected $failures = array(); + + /** + * @var array + */ + protected $notImplemented = array(); + + /** + * @var array + */ + protected $skipped = array(); + + /** + * @var array + */ + protected $listeners = array(); + + /** + * @var integer + */ + protected $runTests = 0; + + /** + * @var float + */ + protected $time = 0; + + /** + * @var PHPUnit_Framework_TestSuite + */ + protected $topTestSuite = NULL; + + /** + * Code Coverage information. + * + * @var PHP_CodeCoverage + */ + protected $codeCoverage; + + /** + * @var boolean + */ + protected $convertErrorsToExceptions = TRUE; + + /** + * @var boolean + */ + protected $stop = FALSE; + + /** + * @var boolean + */ + protected $stopOnError = FALSE; + + /** + * @var boolean + */ + protected $stopOnFailure = FALSE; + + /** + * @var boolean + */ + protected $strictMode = FALSE; + + /** + * @var boolean + */ + protected $stopOnIncomplete = FALSE; + + /** + * @var boolean + */ + protected $stopOnSkipped = FALSE; + + /** + * @var boolean + */ + protected $lastTestFailed = FALSE; + + /** + * @var integer + */ + protected $timeoutForSmallTests = 1; + + /** + * @var integer + */ + protected $timeoutForMediumTests = 10; + + /** + * @var integer + */ + protected $timeoutForLargeTests = 60; + + /** + * Registers a TestListener. + * + * @param PHPUnit_Framework_TestListener + */ + public function addListener(PHPUnit_Framework_TestListener $listener) + { + $this->listeners[] = $listener; + } + + /** + * Unregisters a TestListener. + * + * @param PHPUnit_Framework_TestListener $listener + */ + public function removeListener(PHPUnit_Framework_TestListener $listener) + { + foreach ($this->listeners as $key => $_listener) { + if ($listener === $_listener) { + unset($this->listeners[$key]); + } + } + } + + /** + * Flushes all flushable TestListeners. + * + * @since Method available since Release 3.0.0 + */ + public function flushListeners() + { + foreach ($this->listeners as $listener) { + if ($listener instanceof PHPUnit_Util_Printer) { + $listener->flush(); + } + } + } + + /** + * Adds an error to the list of errors. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($e instanceof PHPUnit_Framework_IncompleteTest) { + $this->notImplemented[] = new PHPUnit_Framework_TestFailure( + $test, $e + ); + + $notifyMethod = 'addIncompleteTest'; + + if ($this->stopOnIncomplete) { + $this->stop(); + } + } + + else if ($e instanceof PHPUnit_Framework_SkippedTest) { + $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addSkippedTest'; + + if ($this->stopOnSkipped) { + $this->stop(); + } + } + + else { + $this->errors[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addError'; + + if ($this->stopOnError || $this->stopOnFailure) { + $this->stop(); + } + } + + foreach ($this->listeners as $listener) { + $listener->$notifyMethod($test, $e, $time); + } + + $this->lastTestFailed = TRUE; + $this->time += $time; + } + + /** + * Adds a failure to the list of failures. + * The passed in exception caused the failure. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if ($e instanceof PHPUnit_Framework_IncompleteTest) { + $this->notImplemented[] = new PHPUnit_Framework_TestFailure( + $test, $e + ); + + $notifyMethod = 'addIncompleteTest'; + + if ($this->stopOnIncomplete) { + $this->stop(); + } + } + + else if ($e instanceof PHPUnit_Framework_SkippedTest) { + $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addSkippedTest'; + + if ($this->stopOnSkipped) { + $this->stop(); + } + } + + else { + $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addFailure'; + + if ($this->stopOnFailure) { + $this->stop(); + } + } + + foreach ($this->listeners as $listener) { + $listener->$notifyMethod($test, $e, $time); + } + + $this->lastTestFailed = TRUE; + $this->time += $time; + } + + /** + * Adds a deprecated feature notice to the list of deprecated features used during run + * + * @param PHPUnit_Util_DeprecatedFeature $deprecatedFeature + */ + public function addDeprecatedFeature(PHPUnit_Util_DeprecatedFeature $deprecatedFeature) + { + $this->deprecatedFeatures[] = $deprecatedFeature; + } + + /** + * Informs the result that a testsuite will be started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + if ($this->topTestSuite === NULL) { + $this->topTestSuite = $suite; + } + + foreach ($this->listeners as $listener) { + $listener->startTestSuite($suite); + } + } + + /** + * Informs the result that a testsuite was completed. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + foreach ($this->listeners as $listener) { + $listener->endTestSuite($suite); + } + } + + /** + * Informs the result that a test will be started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $this->lastTestFailed = FALSE; + $this->runTests += count($test); + + foreach ($this->listeners as $listener) { + $listener->startTest($test); + } + } + + /** + * Informs the result that a test was completed. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + foreach ($this->listeners as $listener) { + $listener->endTest($test, $time); + } + + if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) { + $class = get_class($test); + $key = $class . '::' . $test->getName(); + + $this->passed[$key] = array( + 'result' => $test->getResult(), + 'size' => PHPUnit_Util_Test::getSize( + $class, $test->getName(FALSE) + ) + ); + + $this->time += $time; + } + } + + /** + * Returns TRUE if no incomplete test occured. + * + * @return boolean + */ + public function allCompletlyImplemented() + { + return $this->notImplementedCount() == 0; + } + + /** + * Gets the number of incomplete tests. + * + * @return integer + */ + public function notImplementedCount() + { + return count($this->notImplemented); + } + + /** + * Returns an Enumeration for the incomplete tests. + * + * @return array + */ + public function notImplemented() + { + return $this->notImplemented; + } + + /** + * Returns TRUE if no test has been skipped. + * + * @return boolean + * @since Method available since Release 3.0.0 + */ + public function noneSkipped() + { + return $this->skippedCount() == 0; + } + + /** + * Gets the number of skipped tests. + * + * @return integer + * @since Method available since Release 3.0.0 + */ + public function skippedCount() + { + return count($this->skipped); + } + + /** + * Returns an Enumeration for the skipped tests. + * + * @return array + * @since Method available since Release 3.0.0 + */ + public function skipped() + { + return $this->skipped; + } + + /** + * Gets the number of detected errors. + * + * @return integer + */ + public function errorCount() + { + return count($this->errors); + } + + /** + * Returns an Enumeration for the errors. + * + * @return array + */ + public function errors() + { + return $this->errors; + } + + /** + * Returns an Enumeration for the deprecated features used. + * + * @return array + * @since Method available since Release 3.5.7 + */ + public function deprecatedFeatures() + { + return $this->deprecatedFeatures; + } + + /** + * Returns an Enumeration for the deprecated features used. + * + * @return array + * @since Method available since Release 3.5.7 + */ + public function deprecatedFeaturesCount() + { + return count($this->deprecatedFeatures); + } + + /** + * Gets the number of detected failures. + * + * @return integer + */ + public function failureCount() + { + return count($this->failures); + } + + /** + * Returns an Enumeration for the failures. + * + * @return array + */ + public function failures() + { + return $this->failures; + } + + /** + * Returns the names of the tests that have passed. + * + * @return array + * @since Method available since Release 3.4.0 + */ + public function passed() + { + return $this->passed; + } + + /** + * Returns the (top) test suite. + * + * @return PHPUnit_Framework_TestSuite + * @since Method available since Release 3.0.0 + */ + public function topTestSuite() + { + return $this->topTestSuite; + } + + /** + * Returns whether code coverage information should be collected. + * + * @return boolean If code coverage should be collected + * @since Method available since Release 3.2.0 + */ + public function getCollectCodeCoverageInformation() + { + return $this->codeCoverage !== NULL; + } + + /** + * Returns the strict mode configuration option + * + * @return boolean + */ + public function isStrict() + { + return $this->strictMode; + } + + /** + * Runs a TestCase. + * + * @param PHPUnit_Framework_Test $test + */ + public function run(PHPUnit_Framework_Test $test) + { + PHPUnit_Framework_Assert::resetCount(); + + $error = FALSE; + $failure = FALSE; + $incomplete = FALSE; + $skipped = FALSE; + + $this->startTest($test); + + $errorHandlerSet = FALSE; + + if ($this->convertErrorsToExceptions) { + $oldErrorHandler = set_error_handler( + array('PHPUnit_Util_ErrorHandler', 'handleError'), + E_ALL | E_STRICT + ); + + if ($oldErrorHandler === NULL) { + $errorHandlerSet = TRUE; + } else { + restore_error_handler(); + } + } + + if (self::$xdebugLoaded === NULL) { + self::$xdebugLoaded = extension_loaded('xdebug'); + self::$useXdebug = self::$xdebugLoaded; + } + + $useXdebug = self::$useXdebug && + $this->codeCoverage !== NULL && + !$test instanceof PHPUnit_Extensions_SeleniumTestCase && + !$test instanceof PHPUnit_Framework_Warning; + + if ($useXdebug) { + // We need to blacklist test source files when no whitelist is used. + if (!$this->codeCoverage->filter()->hasWhitelist()) { + $classes = PHPUnit_Util_Class::getHierarchy( + get_class($test), TRUE + ); + + foreach ($classes as $class) { + $this->codeCoverage->filter()->addFileToBlacklist( + $class->getFileName() + ); + } + } + + $this->codeCoverage->start($test); + } + + PHP_Timer::start(); + + try { + if (!$test instanceof PHPUnit_Framework_Warning && + $this->strictMode && + extension_loaded('pcntl') && class_exists('PHP_Invoker')) { + switch ($test->getSize()) { + case PHPUnit_Util_Test::SMALL: { + $_timeout = $this->timeoutForSmallTests; + } + break; + + case PHPUnit_Util_Test::MEDIUM: { + $_timeout = $this->timeoutForMediumTests; + } + break; + + case PHPUnit_Util_Test::LARGE: { + $_timeout = $this->timeoutForLargeTests; + } + break; + } + + $invoker = new PHP_Invoker; + $invoker->invoke(array($test, 'runBare'), array(), $_timeout); + } else { + $test->runBare(); + } + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + $failure = TRUE; + + if ($e instanceof PHPUnit_Framework_IncompleteTestError) { + $incomplete = TRUE; + } + + else if ($e instanceof PHPUnit_Framework_SkippedTestError) { + $skipped = TRUE; + } + } + + catch (Exception $e) { + $error = TRUE; + } + + $time = PHP_Timer::stop(); + $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount()); + + if ($this->strictMode && $test->getNumAssertions() == 0) { + $incomplete = TRUE; + } + + if ($useXdebug) { + try { + $this->codeCoverage->stop(!$incomplete && !$skipped); + } + + catch (PHP_CodeCoverage_Exception $cce) { + $error = TRUE; + + if (!isset($e)) { + $e = $cce; + } + } + } + + if ($errorHandlerSet === TRUE) { + restore_error_handler(); + } + + if ($error === TRUE) { + $this->addError($test, $e, $time); + } + + else if ($failure === TRUE) { + $this->addFailure($test, $e, $time); + } + + else if ($this->strictMode && $test->getNumAssertions() == 0) { + $this->addFailure( + $test, + new PHPUnit_Framework_IncompleteTestError( + 'This test did not perform any assertions' + ), + $time + ); + } + + else if ($this->strictMode && $test->hasOutput()) { + $this->addFailure( + $test, + new PHPUnit_Framework_OutputError( + sprintf( + 'This test printed output: %s', + $test->getActualOutput() + ) + ), + $time + ); + } + + $this->endTest($test, $time); + } + + /** + * Gets the number of run tests. + * + * @return integer + */ + public function count() + { + return $this->runTests; + } + + /** + * Checks whether the test run should stop. + * + * @return boolean + */ + public function shouldStop() + { + return $this->stop; + } + + /** + * Marks that the test run should stop. + * + */ + public function stop() + { + $this->stop = TRUE; + } + + /** + * Returns the PHP_CodeCoverage object. + * + * @return PHP_CodeCoverage + * @since Method available since Release 3.5.0 + */ + public function getCodeCoverage() + { + return $this->codeCoverage; + } + + /** + * Returns the PHP_CodeCoverage object. + * + * @return PHP_CodeCoverage + * @since Method available since Release 3.6.0 + */ + public function setCodeCoverage(PHP_CodeCoverage $codeCoverage) + { + $this->codeCoverage = $codeCoverage; + } + + /** + * Enables or disables the error-to-exception conversion. + * + * @param boolean $flag + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.2.14 + */ + public function convertErrorsToExceptions($flag) + { + if (is_bool($flag)) { + $this->convertErrorsToExceptions = $flag; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Returns the error-to-exception conversion setting. + * + * @return boolean + * @since Method available since Release 3.4.0 + */ + public function getConvertErrorsToExceptions() + { + return $this->convertErrorsToExceptions; + } + + /** + * Enables or disables the stopping when an error occurs. + * + * @param boolean $flag + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.5.0 + */ + public function stopOnError($flag) + { + if (is_bool($flag)) { + $this->stopOnError = $flag; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Enables or disables the stopping when a failure occurs. + * + * @param boolean $flag + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.1.0 + */ + public function stopOnFailure($flag) + { + if (is_bool($flag)) { + $this->stopOnFailure = $flag; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Enables or disables the strict mode. + * + * When active + * * Tests that do not assert anything will be marked as incomplete. + * * Tests that are incomplete or skipped yield no code coverage. + * + * @param boolean $flag + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.5.2 + */ + public function strictMode($flag) + { + if (is_bool($flag)) { + $this->strictMode = $flag; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Enables or disables the stopping for incomplete tests. + * + * @param boolean $flag + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.5.0 + */ + public function stopOnIncomplete($flag) + { + if (is_bool($flag)) { + $this->stopOnIncomplete = $flag; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Enables or disables the stopping for skipped tests. + * + * @param boolean $flag + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.1.0 + */ + public function stopOnSkipped($flag) + { + if (is_bool($flag)) { + $this->stopOnSkipped = $flag; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Returns the time spent running the tests. + * + * @return float + */ + public function time() + { + return $this->time; + } + + /** + * Returns whether the entire test was successful or not. + * + * @return boolean + */ + public function wasSuccessful() + { + return empty($this->errors) && empty($this->failures); + } + + /** + * Sets the timeout for small tests. + * + * @param integer $timeout + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.6.0 + */ + public function setTimeoutForSmallTests($timeout) + { + if (is_integer($timeout)) { + $this->timeoutForSmallTests = $timeout; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + } + + /** + * Sets the timeout for medium tests. + * + * @param integer $timeout + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.6.0 + */ + public function setTimeoutForMediumTests($timeout) + { + if (is_integer($timeout)) { + $this->timeoutForMediumTests = $timeout; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + } + + /** + * Sets the timeout for large tests. + * + * @param integer $timeout + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.6.0 + */ + public function setTimeoutForLargeTests($timeout) + { + if (is_integer($timeout)) { + $this->timeoutForLargeTests = $timeout; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php b/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php new file mode 100644 index 0000000..b78c39b --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php @@ -0,0 +1,950 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A TestSuite is a composite of Tests. It runs a collection of test cases. + * + * Here is an example using the dynamic test definition. + * + * + * addTest(new MathTest('testPass')); + * ?> + * + * + * Alternatively, a TestSuite can extract the tests to be run automatically. + * To do so you pass a ReflectionClass instance for your + * PHPUnit_Framework_TestCase class to the PHPUnit_Framework_TestSuite + * constructor. + * + * + * + * + * + * This constructor creates a suite with all the methods starting with + * "test" that take no arguments. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing, IteratorAggregate +{ + /** + * Enable or disable the backup and restoration of the $GLOBALS array. + * + * @var boolean + */ + protected $backupGlobals = NULL; + + /** + * Enable or disable the backup and restoration of static attributes. + * + * @var boolean + */ + protected $backupStaticAttributes = NULL; + + /** + * The name of the test suite. + * + * @var string + */ + protected $name = ''; + + /** + * The test groups of the test suite. + * + * @var array + */ + protected $groups = array(); + + /** + * The tests in the test suite. + * + * @var array + */ + protected $tests = array(); + + /** + * The number of tests in the test suite. + * + * @var integer + */ + protected $numTests = -1; + + /** + * @var boolean + */ + protected $testCase = FALSE; + + /** + * Constructs a new TestSuite: + * + * - PHPUnit_Framework_TestSuite() constructs an empty TestSuite. + * + * - PHPUnit_Framework_TestSuite(ReflectionClass) constructs a + * TestSuite from the given class. + * + * - PHPUnit_Framework_TestSuite(ReflectionClass, String) + * constructs a TestSuite from the given class with the given + * name. + * + * - PHPUnit_Framework_TestSuite(String) either constructs a + * TestSuite from the given class (if the passed string is the + * name of an existing class) or constructs an empty TestSuite + * with the given name. + * + * @param mixed $theClass + * @param string $name + * @throws PHPUnit_Framework_Exception + */ + public function __construct($theClass = '', $name = '') + { + $argumentsValid = FALSE; + + if (is_object($theClass) && + $theClass instanceof ReflectionClass) { + $argumentsValid = TRUE; + } + + else if (is_string($theClass) && + $theClass !== '' && + class_exists($theClass, FALSE)) { + $argumentsValid = TRUE; + + if ($name == '') { + $name = $theClass; + } + + $theClass = new ReflectionClass($theClass); + } + + else if (is_string($theClass)) { + $this->setName($theClass); + return; + } + + if (!$argumentsValid) { + throw new PHPUnit_Framework_Exception; + } + + if (!$theClass->isSubclassOf('PHPUnit_Framework_TestCase')) { + throw new PHPUnit_Framework_Exception( + 'Class "' . $theClass->name . '" does not extend PHPUnit_Framework_TestCase.' + ); + } + + if ($name != '') { + $this->setName($name); + } else { + $this->setName($theClass->getName()); + } + + $constructor = $theClass->getConstructor(); + + if ($constructor !== NULL && + !$constructor->isPublic()) { + $this->addTest( + self::warning( + sprintf( + 'Class "%s" has no public constructor.', + + $theClass->getName() + ) + ) + ); + + return; + } + + foreach ($theClass->getMethods() as $method) { + $this->addTestMethod($theClass, $method); + } + + if (empty($this->tests)) { + $this->addTest( + self::warning( + sprintf( + 'No tests found in class "%s".', + + $theClass->getName() + ) + ) + ); + } + + $this->testCase = TRUE; + } + + /** + * Returns a string representation of the test suite. + * + * @return string + */ + public function toString() + { + return $this->getName(); + } + + /** + * Adds a test to the suite. + * + * @param PHPUnit_Framework_Test $test + * @param array $groups + */ + public function addTest(PHPUnit_Framework_Test $test, $groups = array()) + { + $class = new ReflectionClass($test); + + if (!$class->isAbstract()) { + $this->tests[] = $test; + $this->numTests = -1; + + if ($test instanceof PHPUnit_Framework_TestSuite && + empty($groups)) { + $groups = $test->getGroups(); + } + + if (empty($groups)) { + $groups = array('__nogroup__'); + } + + foreach ($groups as $group) { + if (!isset($this->groups[$group])) { + $this->groups[$group] = array($test); + } else { + $this->groups[$group][] = $test; + } + } + } + } + + /** + * Adds the tests from the given class to the suite. + * + * @param mixed $testClass + * @throws PHPUnit_Framework_Exception + */ + public function addTestSuite($testClass) + { + if (is_string($testClass) && class_exists($testClass)) { + $testClass = new ReflectionClass($testClass); + } + + if (!is_object($testClass)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'class name or object' + ); + } + + if ($testClass instanceof PHPUnit_Framework_TestSuite) { + $this->addTest($testClass); + } + + else if ($testClass instanceof ReflectionClass) { + $suiteMethod = FALSE; + + if (!$testClass->isAbstract()) { + if ($testClass->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) { + $method = $testClass->getMethod( + PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME + ); + + if ($method->isStatic()) { + $this->addTest( + $method->invoke(NULL, $testClass->getName()) + ); + + $suiteMethod = TRUE; + } + } + } + + if (!$suiteMethod && !$testClass->isAbstract()) { + $this->addTest(new PHPUnit_Framework_TestSuite($testClass)); + } + } + + else { + throw new PHPUnit_Framework_Exception; + } + } + + /** + * Wraps both addTest() and addTestSuite + * as well as the separate import statements for the user's convenience. + * + * If the named file cannot be read or there are no new tests that can be + * added, a PHPUnit_Framework_Warning will be created instead, + * leaving the current test run untouched. + * + * @param string $filename + * @param array $phptOptions Array with ini settings for the php instance + * run, key being the name if the setting, + * value the ini value. + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 2.3.0 + * @author Stefano F. Rausch + */ + public function addTestFile($filename, $phptOptions = array()) + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (file_exists($filename) && substr($filename, -5) == '.phpt') { + $this->addTest( + new PHPUnit_Extensions_PhptTestCase($filename, $phptOptions) + ); + + return; + } + + PHPUnit_Util_Class::collectStart(); + $filename = PHPUnit_Util_Fileloader::checkAndLoad($filename); + $newClasses = PHPUnit_Util_Class::collectEnd(); + $baseName = str_replace('.php', '', basename($filename)); + + foreach ($newClasses as $className) { + if (substr($className, 0 - strlen($baseName)) == $baseName) { + $class = new ReflectionClass($className); + + if ($class->getFileName() == $filename) { + $newClasses = array($className); + break; + } + } + } + + $testsFound = FALSE; + + foreach ($newClasses as $className) { + $class = new ReflectionClass($className); + + if (!$class->isAbstract()) { + if ($class->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) { + $method = $class->getMethod( + PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME + ); + + if ($method->isStatic()) { + $this->addTest($method->invoke(NULL, $className)); + + $testsFound = TRUE; + } + } + + else if ($class->implementsInterface('PHPUnit_Framework_Test')) { + $this->addTestSuite($class); + + $testsFound = TRUE; + } + } + } + + $this->numTests = -1; + } + + /** + * Wrapper for addTestFile() that adds multiple test files. + * + * @param array|Iterator $filenames + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 2.3.0 + */ + public function addTestFiles($filenames) + { + if (!(is_array($filenames) || + (is_object($filenames) && $filenames instanceof Iterator))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, 'array or iterator' + ); + } + + foreach ($filenames as $filename) { + $this->addTestFile((string)$filename); + } + } + + /** + * Counts the number of test cases that will be run by this test. + * + * @return integer + */ + public function count() + { + if ($this->numTests > -1) { + return $this->numTests; + } + + $this->numTests = 0; + + foreach ($this->tests as $test) { + $this->numTests += count($test); + } + + return $this->numTests; + } + + /** + * @param ReflectionClass $theClass + * @param string $name + * @return PHPUnit_Framework_Test + * @throws PHPUnit_Framework_Exception + */ + public static function createTest(ReflectionClass $theClass, $name) + { + $className = $theClass->getName(); + + if (!$theClass->isInstantiable()) { + return self::warning( + sprintf('Cannot instantiate class "%s".', $className) + ); + } + + $backupSettings = PHPUnit_Util_Test::getBackupSettings( + $className, $name + ); + $preserveGlobalState = PHPUnit_Util_Test::getPreserveGlobalStateSettings( + $className, $name + ); + $runTestInSeparateProcess = PHPUnit_Util_Test::getProcessIsolationSettings( + $className, $name + ); + + $constructor = $theClass->getConstructor(); + + if ($constructor !== NULL) { + $parameters = $constructor->getParameters(); + + // TestCase() or TestCase($name) + if (count($parameters) < 2) { + $test = new $className; + } + + // TestCase($name, $data) + else { + try { + $data = PHPUnit_Util_Test::getProvidedData( + $className, $name + ); + } + + catch (Exception $e) { + $message = sprintf( + 'The data provider specified for %s::%s is invalid.', + $className, + $name + ); + + $_message = $e->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::warning($message); + } + + // Test method with @dataProvider. + if (isset($data)) { + $test = new PHPUnit_Framework_TestSuite_DataProvider( + $className . '::' . $name + ); + + if (empty($data)) { + $data = self::warning( + sprintf( + 'No tests found in suite "%s".', + $test->getName() + ) + ); + } + + if ($data instanceof PHPUnit_Framework_Warning) { + $test->addTest($data); + } + + else { + $groups = PHPUnit_Util_Test::getGroups($className, $name); + + foreach ($data as $_dataName => $_data) { + $_test = new $className($name, $_data, $_dataName); + + if ($runTestInSeparateProcess) { + $_test->setRunTestInSeparateProcess(TRUE); + + if ($preserveGlobalState !== NULL) { + $_test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($backupSettings['backupGlobals'] !== NULL) { + $_test->setBackupGlobals( + $backupSettings['backupGlobals'] + ); + } + + if ($backupSettings['backupStaticAttributes'] !== NULL) { + $_test->setBackupStaticAttributes( + $backupSettings['backupStaticAttributes'] + ); + } + + $test->addTest($_test, $groups); + } + } + } + + else { + $test = new $className; + } + } + } + + if (!isset($test)) { + throw new PHPUnit_Framework_Exception('No valid test provided.'); + } + + if ($test instanceof PHPUnit_Framework_TestCase) { + $test->setName($name); + + if ($runTestInSeparateProcess) { + $test->setRunTestInSeparateProcess(TRUE); + + if ($preserveGlobalState !== NULL) { + $test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($backupSettings['backupGlobals'] !== NULL) { + $test->setBackupGlobals($backupSettings['backupGlobals']); + } + + if ($backupSettings['backupStaticAttributes'] !== NULL) { + $test->setBackupStaticAttributes( + $backupSettings['backupStaticAttributes'] + ); + } + } + + return $test; + } + + /** + * Creates a default TestResult object. + * + * @return PHPUnit_Framework_TestResult + */ + protected function createResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * Returns the name of the suite. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the test groups of the suite. + * + * @return array + * @since Method available since Release 3.2.0 + */ + public function getGroups() + { + return array_keys($this->groups); + } + + /** + * Runs the tests and collects their result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + * @param mixed $filter + * @param array $groups + * @param array $excludeGroups + * @param boolean $processIsolation + * @return PHPUnit_Framework_TestResult + * @throws PHPUnit_Framework_Exception + */ + public function run(PHPUnit_Framework_TestResult $result = NULL, $filter = FALSE, array $groups = array(), array $excludeGroups = array(), $processIsolation = FALSE) + { + if ($result === NULL) { + $result = $this->createResult(); + } + + $result->startTestSuite($this); + + $doSetup = TRUE; + + if (!empty($excludeGroups)) { + foreach ($this->groups as $_group => $_tests) { + if (in_array($_group, $excludeGroups) && + count($_tests) == count($this->tests)) { + $doSetup = FALSE; + } + } + } + + if ($doSetup) { + try { + $this->setUp(); + + if ($this->testCase && + // Some extensions use test names that are not classes; + // The method_exists() triggers an autoload call that causes issues with die()ing autoloaders. + class_exists($this->name, false) && + method_exists($this->name, 'setUpBeforeClass')) { + call_user_func(array($this->name, 'setUpBeforeClass')); + } + } + + catch (PHPUnit_Framework_SkippedTestSuiteError $e) { + $numTests = count($this); + + for ($i = 0; $i < $numTests; $i++) { + $result->addFailure($this, $e, 0); + } + + return $result; + } + + catch (Exception $e) { + $numTests = count($this); + + for ($i = 0; $i < $numTests; $i++) { + $result->addError($this, $e, 0); + } + + return $result; + } + } + + if (empty($groups)) { + $tests = $this->tests; + } else { + $tests = new SplObjectStorage; + + foreach ($groups as $group) { + if (isset($this->groups[$group])) { + foreach ($this->groups[$group] as $test) { + $tests->attach($test); + } + } + } + } + + foreach ($tests as $test) { + if ($result->shouldStop()) { + break; + } + + if ($test instanceof PHPUnit_Framework_TestSuite) { + $test->setBackupGlobals($this->backupGlobals); + $test->setBackupStaticAttributes($this->backupStaticAttributes); + + $test->run( + $result, $filter, $groups, $excludeGroups, $processIsolation + ); + } else { + $runTest = TRUE; + + if ($filter !== FALSE ) { + $tmp = PHPUnit_Util_Test::describe($test, FALSE); + + if ($tmp[0] != '') { + $name = join('::', $tmp); + } else { + $name = $tmp[1]; + } + + if (preg_match($filter, $name) == 0) { + $runTest = FALSE; + } + } + + if ($runTest && !empty($excludeGroups)) { + foreach ($this->groups as $_group => $_tests) { + if (in_array($_group, $excludeGroups)) { + foreach ($_tests as $_test) { + if ($test === $_test) { + $runTest = FALSE; + break 2; + } + } + } + } + } + + if ($runTest) { + if ($test instanceof PHPUnit_Framework_TestCase) { + $test->setBackupGlobals($this->backupGlobals); + $test->setBackupStaticAttributes( + $this->backupStaticAttributes + ); + $test->setRunTestInSeparateProcess($processIsolation); + } + + $this->runTest($test, $result); + } + } + } + + if ($doSetup) { + if ($this->testCase && + // Some extensions use test names that are not classes; + // The method_exists() triggers an autoload call that causes issues with die()ing autoloaders. + class_exists($this->name, false) && + method_exists($this->name, 'tearDownAfterClass')) { + call_user_func(array($this->name, 'tearDownAfterClass')); + } + + $this->tearDown(); + } + + $result->endTestSuite($this); + + return $result; + } + + /** + * Runs a test. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_TestResult $result + */ + public function runTest(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result) + { + $test->run($result); + } + + /** + * Sets the name of the suite. + * + * @param string + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Returns the test at the given index. + * + * @param integer + * @return PHPUnit_Framework_Test + */ + public function testAt($index) + { + if (isset($this->tests[$index])) { + return $this->tests[$index]; + } else { + return FALSE; + } + } + + /** + * Returns the tests as an enumeration. + * + * @return array + */ + public function tests() + { + return $this->tests; + } + + /** + * Mark the test suite as skipped. + * + * @param string $message + * @throws PHPUnit_Framework_SkippedTestSuiteError + * @since Method available since Release 3.0.0 + */ + public function markTestSuiteSkipped($message = '') + { + throw new PHPUnit_Framework_SkippedTestSuiteError($message); + } + + /** + * @param ReflectionClass $class + * @param ReflectionMethod $method + */ + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) + { + $name = $method->getName(); + + if ($this->isPublicTestMethod($method)) { + $test = self::createTest($class, $name); + + if ($test instanceof PHPUnit_Framework_TestCase || + $test instanceof PHPUnit_Framework_TestSuite_DataProvider) { + $test->setDependencies( + PHPUnit_Util_Test::getDependencies($class->getName(), $name) + ); + } + + $this->addTest($test, PHPUnit_Util_Test::getGroups( + $class->getName(), $name) + ); + } + + else if ($this->isTestMethod($method)) { + $this->addTest( + self::warning( + sprintf( + 'Test method "%s" in test class "%s" is not public.', + $name, + $class->getName() + ) + ) + ); + } + } + + /** + * @param ReflectionMethod $method + * @return boolean + */ + public static function isPublicTestMethod(ReflectionMethod $method) + { + return (self::isTestMethod($method) && $method->isPublic()); + } + + /** + * @param ReflectionMethod $method + * @return boolean + */ + public static function isTestMethod(ReflectionMethod $method) + { + if (strpos($method->name, 'test') === 0) { + return TRUE; + } + + // @scenario on TestCase::testMethod() + // @test on TestCase::testMethod() + return strpos($method->getDocComment(), '@test') !== FALSE || + strpos($method->getDocComment(), '@scenario') !== FALSE; + } + + /** + * @param string $message + * @return PHPUnit_Framework_Warning + */ + protected static function warning($message) + { + return new PHPUnit_Framework_Warning($message); + } + + /** + * @param boolean $backupGlobals + * @since Method available since Release 3.3.0 + */ + public function setBackupGlobals($backupGlobals) + { + if (is_null($this->backupGlobals) && is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; + } + } + + /** + * @param boolean $backupStaticAttributes + * @since Method available since Release 3.4.0 + */ + public function setBackupStaticAttributes($backupStaticAttributes) + { + if (is_null($this->backupStaticAttributes) && + is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; + } + } + + /** + * Returns an iterator for this test suite. + * + * @return RecursiveIteratorIterator + * @since Method available since Release 3.1.0 + */ + public function getIterator() + { + return new RecursiveIteratorIterator( + new PHPUnit_Util_TestSuiteIterator($this) + ); + } + + /** + * Template Method that is called before the tests + * of this test suite are run. + * + * @since Method available since Release 3.1.0 + */ + protected function setUp() + { + } + + /** + * Template Method that is called after the tests + * of this test suite have finished running. + * + * @since Method available since Release 3.1.0 + */ + protected function tearDown() + { + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite/DataProvider.php b/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite/DataProvider.php new file mode 100644 index 0000000..31f0808 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite/DataProvider.php @@ -0,0 +1,70 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework_TestSuite + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Framework_TestSuite + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_TestSuite_DataProvider extends PHPUnit_Framework_TestSuite +{ + /** + * Sets the dependencies of a TestCase. + * + * @param array $dependencies + */ + public function setDependencies(array $dependencies) + { + foreach ($this->tests as $test) { + $test->setDependencies($dependencies); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Framework/Warning.php b/vendor/phpunit/phpunit/PHPUnit/Framework/Warning.php new file mode 100644 index 0000000..72b30d8 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Framework/Warning.php @@ -0,0 +1,125 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A warning. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_Warning extends PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var boolean + */ + protected $backupGlobals = FALSE; + + /** + * @var boolean + */ + protected $backupStaticAttributes = FALSE; + + /** + * @var boolean + */ + protected $runTestInSeparateProcess = FALSE; + + /** + * @var boolean + */ + protected $useErrorHandler = FALSE; + + /** + * @var boolean + */ + protected $useOutputBuffering = FALSE; + + /** + * @param string $message + */ + public function __construct($message = '') + { + $this->message = $message; + parent::__construct('Warning'); + } + + /** + * @throws PHPUnit_Framework_Exception + */ + protected function runTest() + { + $this->fail($this->message); + } + + /** + * @return string + * @since Method available since Release 3.0.0 + */ + public function getMessage() + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + * + * @return string + * @since Method available since Release 3.4.0 + */ + public function toString() + { + return 'Warning'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Runner/BaseTestRunner.php b/vendor/phpunit/phpunit/PHPUnit/Runner/BaseTestRunner.php new file mode 100644 index 0000000..c80af8f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Runner/BaseTestRunner.php @@ -0,0 +1,189 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Base class for all test runners. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +abstract class PHPUnit_Runner_BaseTestRunner +{ + const STATUS_PASSED = 0; + const STATUS_SKIPPED = 1; + const STATUS_INCOMPLETE = 2; + const STATUS_FAILURE = 3; + const STATUS_ERROR = 4; + const SUITE_METHODNAME = 'suite'; + + /** + * Returns the loader to be used. + * + * @return PHPUnit_Runner_TestSuiteLoader + */ + public function getLoader() + { + return new PHPUnit_Runner_StandardTestSuiteLoader; + } + + /** + * Returns the Test corresponding to the given suite. + * This is a template method, subclasses override + * the runFailed() and clearStatus() methods. + * + * @param string $suiteClassName + * @param string $suiteClassFile + * @param mixed $suffixes + * @return PHPUnit_Framework_Test + */ + public function getTest($suiteClassName, $suiteClassFile = '', $suffixes = '') + { + if (is_dir($suiteClassName) && + !is_file($suiteClassName . '.php') && empty($suiteClassFile)) { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $suiteClassName, $suffixes + ); + + $suite = new PHPUnit_Framework_TestSuite($suiteClassName); + $suite->addTestFiles($files); + + return $suite; + } + + try { + $testClass = $this->loadSuiteClass( + $suiteClassName, $suiteClassFile + ); + } + + catch (Exception $e) { + $this->runFailed($e->getMessage()); + return NULL; + } + + try { + $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); + + if (!$suiteMethod->isStatic()) { + $this->runFailed( + 'suite() method must be static.' + ); + + return NULL; + } + + try { + $test = $suiteMethod->invoke(NULL, $testClass->getName()); + } + + catch (ReflectionException $e) { + $this->runFailed( + sprintf( + "Failed to invoke suite() method.\n%s", + + $e->getMessage() + ) + ); + + return NULL; + } + } + + catch (ReflectionException $e) { + try { + $test = new PHPUnit_Framework_TestSuite($testClass); + } + + catch (PHPUnit_Framework_Exception $e) { + $test = new PHPUnit_Framework_TestSuite; + $test->setName($suiteClassName); + } + } + + $this->clearStatus(); + + return $test; + } + + /** + * Returns the loaded ReflectionClass for a suite name. + * + * @param string $suiteClassName + * @param string $suiteClassFile + * @return ReflectionClass + */ + protected function loadSuiteClass($suiteClassName, $suiteClassFile = '') + { + $loader = $this->getLoader(); + + if ($loader instanceof PHPUnit_Runner_StandardTestSuiteLoader) { + return $loader->load($suiteClassName, $suiteClassFile); + } else { + return $loader->load($suiteClassName, $suiteClassFile); + } + } + + /** + * Clears the status message. + * + */ + protected function clearStatus() + { + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + * + * @param string $message + */ + abstract protected function runFailed($message); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Runner/StandardTestSuiteLoader.php b/vendor/phpunit/phpunit/PHPUnit/Runner/StandardTestSuiteLoader.php new file mode 100644 index 0000000..88574e2 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Runner/StandardTestSuiteLoader.php @@ -0,0 +1,153 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * The standard test suite loader. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Runner_StandardTestSuiteLoader implements PHPUnit_Runner_TestSuiteLoader +{ + /** + * @param string $suiteClassName + * @param string $suiteClassFile + * @return ReflectionClass + * @throws PHPUnit_Framework_Exception + */ + public function load($suiteClassName, $suiteClassFile = '') + { + $suiteClassName = str_replace('.php', '', $suiteClassName); + + if (empty($suiteClassFile)) { + $suiteClassFile = PHPUnit_Util_Filesystem::classNameToFilename( + $suiteClassName + ); + } + + if (!class_exists($suiteClassName, FALSE)) { + PHPUnit_Util_Class::collectStart(); + $filename = PHPUnit_Util_Fileloader::checkAndLoad($suiteClassFile); + $loadedClasses = PHPUnit_Util_Class::collectEnd(); + } + + if (!class_exists($suiteClassName, FALSE) && !empty($loadedClasses)) { + $offset = 0 - strlen($suiteClassName); + + foreach ($loadedClasses as $loadedClass) { + $class = new ReflectionClass($loadedClass); + if (substr($loadedClass, $offset) === $suiteClassName && + $class->getFileName() == $filename) { + $suiteClassName = $loadedClass; + break; + } + } + } + + if (!class_exists($suiteClassName, FALSE) && !empty($loadedClasses)) { + $testCaseClass = 'PHPUnit_Framework_TestCase'; + + foreach ($loadedClasses as $loadedClass) { + $class = new ReflectionClass($loadedClass); + $classFile = $class->getFileName(); + + if ($class->isSubclassOf($testCaseClass) && + !$class->isAbstract()) { + $suiteClassName = $loadedClass; + $testCaseClass = $loadedClass; + + if ($classFile == realpath($suiteClassFile)) { + break; + } + } + + if ($class->hasMethod('suite')) { + $method = $class->getMethod('suite'); + + if (!$method->isAbstract() && + $method->isPublic() && + $method->isStatic()) { + $suiteClassName = $loadedClass; + + if ($classFile == realpath($suiteClassFile)) { + break; + } + } + } + } + } + + if (class_exists($suiteClassName, FALSE)) { + $class = new ReflectionClass($suiteClassName); + + if ($class->getFileName() == realpath($suiteClassFile)) { + return $class; + } + } + + throw new PHPUnit_Framework_Exception( + sprintf( + "Class '%s' could not be found in '%s'.", + + $suiteClassName, + $suiteClassFile + ) + ); + } + + /** + * @param ReflectionClass $aClass + * @return ReflectionClass + */ + public function reload(ReflectionClass $aClass) + { + return $aClass; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Runner/TestSuiteLoader.php b/vendor/phpunit/phpunit/PHPUnit/Runner/TestSuiteLoader.php new file mode 100644 index 0000000..7cad44f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Runner/TestSuiteLoader.php @@ -0,0 +1,71 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * An interface to define how a test suite should be loaded. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Runner_TestSuiteLoader +{ + /** + * @param string $suiteClassName + * @param string $suiteClassFile + * @return ReflectionClass + */ + public function load($suiteClassName, $suiteClassFile = ''); + + /** + * @param ReflectionClass $aClass + * @return ReflectionClass + */ + public function reload(ReflectionClass $aClass); +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Runner/Version.php b/vendor/phpunit/phpunit/PHPUnit/Runner/Version.php new file mode 100644 index 0000000..3691c32 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Runner/Version.php @@ -0,0 +1,100 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * This class defines the current version of PHPUnit. + * + * @package PHPUnit + * @subpackage Runner + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Runner_Version +{ + const VERSION = '3.7.8'; + protected static $version; + + /** + * Returns the current version of PHPUnit. + * + * @return string + */ + public static function id() + { + if (self::$version === NULL) { + self::$version = self::VERSION; + + if (is_dir(dirname(dirname(__DIR__)) . '/.git')) { + $dir = getcwd(); + chdir(__DIR__); + $version = exec('git describe --tags'); + chdir($dir); + + if ($version) { + if (count(explode('.', self::VERSION)) == 3) { + self::$version = $version; + } else { + $version = explode('-', $version); + + self::$version = self::VERSION . '-' . $version[2]; + } + } + } + } + + return self::$version; + } + + /** + * @return string + */ + public static function getVersionString() + { + return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann.'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php b/vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php new file mode 100644 index 0000000..cc5bac7 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/TextUI/Command.php @@ -0,0 +1,897 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage TextUI + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * A TestRunner for the Command Line Interface (CLI) + * PHP SAPI Module. + * + * @package PHPUnit + * @subpackage TextUI + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_TextUI_Command +{ + /** + * @var array + */ + protected $arguments = array( + 'listGroups' => FALSE, + 'loader' => NULL, + 'useDefaultConfiguration' => TRUE + ); + + /** + * @var array + */ + protected $options = array(); + + /** + * @var array + */ + protected $longOptions = array( + 'colors' => NULL, + 'bootstrap=' => NULL, + 'configuration=' => NULL, + 'coverage-html=' => NULL, + 'coverage-clover=' => NULL, + 'coverage-php=' => NULL, + 'coverage-text==' => NULL, + 'debug' => NULL, + 'exclude-group=' => NULL, + 'filter=' => NULL, + 'testsuite=' => NULL, + 'group=' => NULL, + 'help' => NULL, + 'include-path=' => NULL, + 'list-groups' => NULL, + 'loader=' => NULL, + 'log-json=' => NULL, + 'log-junit=' => NULL, + 'log-tap=' => NULL, + 'process-isolation' => NULL, + 'repeat=' => NULL, + 'stderr' => NULL, + 'stop-on-error' => NULL, + 'stop-on-failure' => NULL, + 'stop-on-incomplete' => NULL, + 'stop-on-skipped' => NULL, + 'strict' => NULL, + 'tap' => NULL, + 'testdox' => NULL, + 'testdox-html=' => NULL, + 'testdox-text=' => NULL, + 'test-suffix=' => NULL, + 'no-configuration' => NULL, + 'no-globals-backup' => NULL, + 'printer=' => NULL, + 'static-backup' => NULL, + 'verbose' => NULL, + 'version' => NULL + ); + + /** + * @var array + */ + protected $missingExtensions = array(); + + /** + * @param boolean $exit + */ + public static function main($exit = TRUE) + { + $command = new PHPUnit_TextUI_Command; + return $command->run($_SERVER['argv'], $exit); + } + + /** + * @param array $argv + * @param boolean $exit + */ + public function run(array $argv, $exit = TRUE) + { + $this->handleArguments($argv); + + $runner = $this->createRunner(); + + if (is_object($this->arguments['test']) && + $this->arguments['test'] instanceof PHPUnit_Framework_Test) { + $suite = $this->arguments['test']; + } else { + $suite = $runner->getTest( + $this->arguments['test'], + $this->arguments['testFile'], + $this->arguments['testSuffixes'] + ); + } + + if ($this->arguments['listGroups']) { + PHPUnit_TextUI_TestRunner::printVersionString(); + + print "Available test group(s):\n"; + + $groups = $suite->getGroups(); + sort($groups); + + foreach ($groups as $group) { + print " - $group\n"; + } + + if ($exit) { + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } else { + return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; + } + } + + unset($this->arguments['test']); + unset($this->arguments['testFile']); + + try { + $result = $runner->doRun($suite, $this->arguments); + } + + catch (PHPUnit_Framework_Exception $e) { + print $e->getMessage() . "\n"; + } + + $ret = PHPUnit_TextUI_TestRunner::FAILURE_EXIT; + + if (isset($result) && $result->wasSuccessful()) { + $ret = PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; + } + + else if (!isset($result) || $result->errorCount() > 0) { + $ret = PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT; + } + + if ($exit) { + exit($ret); + } else { + return $ret; + } + } + + /** + * Create a TestRunner, override in subclasses. + * + * @return PHPUnit_TextUI_TestRunner + * @since Method available since Release 3.6.0 + */ + protected function createRunner() + { + return new PHPUnit_TextUI_TestRunner($this->arguments['loader']); + } + + /** + * Handles the command-line arguments. + * + * A child class of PHPUnit_TextUI_Command can hook into the argument + * parsing by adding the switch(es) to the $longOptions array and point to a + * callback method that handles the switch(es) in the child class like this + * + * + * longOptions['--my-switch'] = 'myHandler'; + * } + * + * // --my-switch foo -> myHandler('foo') + * protected function myHandler($value) + * { + * } + * } + * + * + * @param array $argv + */ + protected function handleArguments(array $argv) + { + try { + $this->options = PHPUnit_Util_Getopt::getopt( + $argv, + 'd:c:hv', + array_keys($this->longOptions) + ); + } + + catch (PHPUnit_Framework_Exception $e) { + PHPUnit_TextUI_TestRunner::showError($e->getMessage()); + } + + foreach ($this->options[0] as $option) { + switch ($option[0]) { + case '--colors': { + $this->arguments['colors'] = TRUE; + } + break; + + case '--bootstrap': { + $this->arguments['bootstrap'] = $option[1]; + } + break; + + case 'c': + case '--configuration': { + $this->arguments['configuration'] = $option[1]; + } + break; + + case '--coverage-clover': + case '--coverage-html': + case '--coverage-php': + case '--coverage-text': { + if (!extension_loaded('tokenizer')) { + $this->showExtensionNotLoadedMessage( + 'tokenizer', 'No code coverage will be generated.' + ); + + continue; + } + + if (!extension_loaded('xdebug')) { + $this->showExtensionNotLoadedMessage( + 'Xdebug', 'No code coverage will be generated.' + ); + + continue; + } + + switch ($option[0]) { + case '--coverage-clover': { + $this->arguments['coverageClover'] = $option[1]; + } + break; + + case '--coverage-html': { + $this->arguments['reportDirectory'] = $option[1]; + } + break; + + case '--coverage-php': { + $this->arguments['coveragePHP'] = $option[1]; + } + break; + + case '--coverage-text': { + if ($option[1] === NULL) { + $option[1] = 'php://stdout'; + } + + $this->arguments['coverageText'] = $option[1]; + $this->arguments['coverageTextShowUncoveredFiles'] = FALSE; + } + break; + } + } + break; + + case 'd': { + $ini = explode('=', $option[1]); + + if (isset($ini[0])) { + if (isset($ini[1])) { + ini_set($ini[0], $ini[1]); + } else { + ini_set($ini[0], TRUE); + } + } + } + break; + + case '--debug': { + $this->arguments['debug'] = TRUE; + } + break; + + case 'h': + case '--help': { + $this->showHelp(); + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } + break; + + case '--filter': { + $this->arguments['filter'] = $option[1]; + } + break; + + case '--testsuite': { + $this->arguments['testsuite'] = $option[1]; + } + break; + + case '--group': { + $this->arguments['groups'] = explode(',', $option[1]); + } + break; + + case '--exclude-group': { + $this->arguments['excludeGroups'] = explode( + ',', $option[1] + ); + } + break; + + case '--test-suffix': { + $this->arguments['testSuffixes'] = explode( + ',', $option[1] + ); + } + break; + + case '--include-path': { + $includePath = $option[1]; + } + break; + + case '--list-groups': { + $this->arguments['listGroups'] = TRUE; + } + break; + + case '--printer': { + $this->arguments['printer'] = $option[1]; + } + break; + + case '--loader': { + $this->arguments['loader'] = $option[1]; + } + break; + + case '--log-json': { + $this->arguments['jsonLogfile'] = $option[1]; + } + break; + + case '--log-junit': { + $this->arguments['junitLogfile'] = $option[1]; + } + break; + + case '--log-tap': { + $this->arguments['tapLogfile'] = $option[1]; + } + break; + + case '--process-isolation': { + $this->arguments['processIsolation'] = TRUE; + } + break; + + case '--repeat': { + $this->arguments['repeat'] = (int)$option[1]; + } + break; + + case '--stderr': { + $this->arguments['printer'] = new PHPUnit_TextUI_ResultPrinter( + 'php://stderr', + isset($this->arguments['verbose']) ? $this->arguments['verbose'] : FALSE + ); + } + break; + + case '--stop-on-error': { + $this->arguments['stopOnError'] = TRUE; + } + break; + + case '--stop-on-failure': { + $this->arguments['stopOnFailure'] = TRUE; + } + break; + + case '--stop-on-incomplete': { + $this->arguments['stopOnIncomplete'] = TRUE; + } + break; + + case '--stop-on-skipped': { + $this->arguments['stopOnSkipped'] = TRUE; + } + break; + + case '--tap': { + $this->arguments['printer'] = new PHPUnit_Util_Log_TAP; + } + break; + + case '--testdox': { + $this->arguments['printer'] = new PHPUnit_Util_TestDox_ResultPrinter_Text; + } + break; + + case '--testdox-html': { + $this->arguments['testdoxHTMLFile'] = $option[1]; + } + break; + + case '--testdox-text': { + $this->arguments['testdoxTextFile'] = $option[1]; + } + break; + + case '--no-configuration': { + $this->arguments['useDefaultConfiguration'] = FALSE; + } + break; + + case '--no-globals-backup': { + $this->arguments['backupGlobals'] = FALSE; + } + break; + + case '--static-backup': { + $this->arguments['backupStaticAttributes'] = TRUE; + } + break; + + case 'v': + case '--verbose': { + $this->arguments['verbose'] = TRUE; + } + break; + + case '--version': { + PHPUnit_TextUI_TestRunner::printVersionString(); + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } + break; + + case '--strict': { + $this->arguments['strict'] = TRUE; + } + break; + + default: { + $optionName = str_replace('--', '', $option[0]); + + if (isset($this->longOptions[$optionName])) { + $handler = $this->longOptions[$optionName]; + } + + else if (isset($this->longOptions[$optionName . '='])) { + $handler = $this->longOptions[$optionName . '=']; + } + + if (isset($handler) && is_callable(array($this, $handler))) { + $this->$handler($option[1]); + } + } + } + } + + $this->handleCustomTestSuite(); + + if (!isset($this->arguments['test'])) { + + if (isset($this->options[1][0])) { + $this->arguments['test'] = $this->options[1][0]; + } + + if (isset($this->options[1][1])) { + $this->arguments['testFile'] = $this->options[1][1]; + } else { + $this->arguments['testFile'] = ''; + } + + if (isset($this->arguments['test']) && + is_file($this->arguments['test']) && + substr($this->arguments['test'], -5, 5) != '.phpt') { + $this->arguments['testFile'] = realpath($this->arguments['test']); + $this->arguments['test'] = substr($this->arguments['test'], 0, strrpos($this->arguments['test'], '.')); + } + } + + if (!isset($this->arguments['testSuffixes'])) { + $this->arguments['testSuffixes'] = array('Test.php', '.phpt'); + } + + if (isset($includePath)) { + ini_set( + 'include_path', + $includePath . PATH_SEPARATOR . ini_get('include_path') + ); + } + + if (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } + + if (isset($this->arguments['printer']) && + is_string($this->arguments['printer'])) { + $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); + } + + if ($this->arguments['loader'] !== NULL) { + $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); + } + + if (isset($this->arguments['configuration']) && + is_dir($this->arguments['configuration'])) { + $configurationFile = $this->arguments['configuration'] . + '/phpunit.xml'; + + if (file_exists($configurationFile)) { + $this->arguments['configuration'] = realpath( + $configurationFile + ); + } + + else if (file_exists($configurationFile . '.dist')) { + $this->arguments['configuration'] = realpath( + $configurationFile . '.dist' + ); + } + } + + else if (!isset($this->arguments['configuration']) && + $this->arguments['useDefaultConfiguration']) { + if (file_exists('phpunit.xml')) { + $this->arguments['configuration'] = realpath('phpunit.xml'); + } else if (file_exists('phpunit.xml.dist')) { + $this->arguments['configuration'] = realpath( + 'phpunit.xml.dist' + ); + } + } + + if (isset($this->arguments['configuration'])) { + try { + $configuration = PHPUnit_Util_Configuration::getInstance( + $this->arguments['configuration'] + ); + } + + catch (Exception $e) { + print $e->getMessage() . "\n"; + exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); + } + + $phpunit = $configuration->getPHPUnitConfiguration(); + + $configuration->handlePHPConfiguration(); + + if (!isset($this->arguments['bootstrap']) && isset($phpunit['bootstrap'])) { + $this->handleBootstrap($phpunit['bootstrap']); + } + + if (isset($phpunit['printerClass'])) { + if (isset($phpunit['printerFile'])) { + $file = $phpunit['printerFile']; + } else { + $file = ''; + } + + $this->arguments['printer'] = $this->handlePrinter( + $phpunit['printerClass'], $file + ); + } + + if (isset($phpunit['testSuiteLoaderClass'])) { + if (isset($phpunit['testSuiteLoaderFile'])) { + $file = $phpunit['testSuiteLoaderFile']; + } else { + $file = ''; + } + + $this->arguments['loader'] = $this->handleLoader( + $phpunit['testSuiteLoaderClass'], $file + ); + } + + $logging = $configuration->getLoggingConfiguration(); + + if (isset($logging['coverage-html']) || isset($logging['coverage-clover']) || isset($logging['coverage-text']) ) { + if (!extension_loaded('tokenizer')) { + $this->showExtensionNotLoadedMessage( + 'tokenizer', 'No code coverage will be generated.' + ); + } + + else if (!extension_loaded('Xdebug')) { + $this->showExtensionNotLoadedMessage( + 'Xdebug', 'No code coverage will be generated.' + ); + } + } + + $browsers = $configuration->getSeleniumBrowserConfiguration(); + + if (!empty($browsers) && + class_exists('PHPUnit_Extensions_SeleniumTestCase')) { + PHPUnit_Extensions_SeleniumTestCase::$browsers = $browsers; + } + + if (!isset($this->arguments['test'])) { + $testSuite = $configuration->getTestSuiteConfiguration(isset($this->arguments['testsuite']) ? $this->arguments['testsuite'] : null); + + if ($testSuite !== NULL) { + $this->arguments['test'] = $testSuite; + } + } + } + + if (isset($this->arguments['test']) && is_string($this->arguments['test']) && substr($this->arguments['test'], -5, 5) == '.phpt') { + $test = new PHPUnit_Extensions_PhptTestCase($this->arguments['test']); + + $this->arguments['test'] = new PHPUnit_Framework_TestSuite; + $this->arguments['test']->addTest($test); + } + + if (!isset($this->arguments['test']) || + (isset($this->arguments['testDatabaseLogRevision']) && !isset($this->arguments['testDatabaseDSN']))) { + $this->showHelp(); + exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); + } + } + + /** + * Handles the loading of the PHPUnit_Runner_TestSuiteLoader implementation. + * + * @param string $loaderClass + * @param string $loaderFile + * @return PHPUnit_Runner_TestSuiteLoader + */ + protected function handleLoader($loaderClass, $loaderFile = '') + { + if (!class_exists($loaderClass, FALSE)) { + if ($loaderFile == '') { + $loaderFile = PHPUnit_Util_Filesystem::classNameToFilename( + $loaderClass + ); + } + + $loaderFile = stream_resolve_include_path($loaderFile); + + if ($loaderFile) { + require $loaderFile; + } + } + + if (class_exists($loaderClass, FALSE)) { + $class = new ReflectionClass($loaderClass); + + if ($class->implementsInterface('PHPUnit_Runner_TestSuiteLoader') && + $class->isInstantiable()) { + $loader = $class->newInstance(); + } + } + + if (!isset($loader)) { + PHPUnit_TextUI_TestRunner::showError( + sprintf( + 'Could not use "%s" as loader.', + + $loaderClass + ) + ); + } + + return $loader; + } + + /** + * Handles the loading of the PHPUnit_Util_Printer implementation. + * + * @param string $printerClass + * @param string $printerFile + * @return PHPUnit_Util_Printer + */ + protected function handlePrinter($printerClass, $printerFile = '') + { + if (!class_exists($printerClass, FALSE)) { + if ($printerFile == '') { + $printerFile = PHPUnit_Util_Filesystem::classNameToFilename( + $printerClass + ); + } + + $printerFile = stream_resolve_include_path($printerFile); + + if ($printerFile) { + require $printerFile; + } + } + + if (class_exists($printerClass, FALSE)) { + $class = new ReflectionClass($printerClass); + + if ($class->implementsInterface('PHPUnit_Framework_TestListener') && + $class->isSubclassOf('PHPUnit_Util_Printer') && + $class->isInstantiable()) { + $printer = $class->newInstance(); + } + } + + if (!isset($printer)) { + PHPUnit_TextUI_TestRunner::showError( + sprintf( + 'Could not use "%s" as printer.', + + $printerClass + ) + ); + } + + return $printer; + } + + /** + * Loads a bootstrap file. + * + * @param string $filename + */ + protected function handleBootstrap($filename) + { + try { + PHPUnit_Util_Fileloader::checkAndLoad($filename); + } + + catch (PHPUnit_Framework_Exception $e) { + PHPUnit_TextUI_TestRunner::showError($e->getMessage()); + } + } + + /** + * @param string $message + * @since Method available since Release 3.6.0 + */ + protected function showExtensionNotLoadedMessage($extension, $message = '') + { + if (isset($this->missingExtensions[$extension])) { + return; + } + + if (!empty($message)) { + $message = ' ' . $message; + } + + $this->showMessage( + 'The ' . $extension . ' extension is not loaded.' . $message . "\n", + FALSE + ); + + $this->missingExtensions[$extension] = TRUE; + } + + /** + * Shows a message. + * + * @param string $message + * @param boolean $exit + */ + protected function showMessage($message, $exit = TRUE) + { + PHPUnit_TextUI_TestRunner::printVersionString(); + print $message . "\n"; + + if ($exit) { + exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); + } else { + print "\n"; + } + } + + /** + * Show the help message. + */ + protected function showHelp() + { + PHPUnit_TextUI_TestRunner::printVersionString(); + + print << + + --log-junit Log test execution in JUnit XML format to file. + --log-tap Log test execution in TAP format to file. + --log-json Log test execution in JSON format. + + --coverage-clover Generate code coverage report in Clover XML format. + --coverage-html Generate code coverage report in HTML format. + --coverage-php Serialize PHP_CodeCoverage object to file. + --coverage-text= Generate code coverage report in text format. + Default to writing to the standard output. + + --testdox-html Write agile documentation in HTML format to file. + --testdox-text Write agile documentation in Text format to file. + + --filter Filter which tests to run. + --testsuite Filter which testsuite to run. + --group ... Only runs tests from the specified group(s). + --exclude-group ... Exclude tests from the specified group(s). + --list-groups List available test groups. + --test-suffix ... Only search for test in files with specified + suffix(es). Default: Test.php,.phpt + + --loader TestSuiteLoader implementation to use. + --printer TestSuiteListener implementation to use. + --repeat Runs the test(s) repeatedly. + + --tap Report test execution progress in TAP format. + --testdox Report test execution progress in TestDox format. + + --colors Use colors in output. + --stderr Write to STDERR instead of STDOUT. + --stop-on-error Stop execution upon first error. + --stop-on-failure Stop execution upon first error or failure. + --stop-on-skipped Stop execution upon first skipped test. + --stop-on-incomplete Stop execution upon first incomplete test. + --strict Run tests in strict mode. + -v|--verbose Output more verbose information. + --debug Display debugging information during test execution. + + --process-isolation Run each test in a separate PHP process. + --no-globals-backup Do not backup and restore \$GLOBALS for each test. + --static-backup Backup and restore static attributes for each test. + + --bootstrap A "bootstrap" PHP file that is run before the tests. + -c|--configuration Read configuration from XML file. + --no-configuration Ignore default configuration file (phpunit.xml). + --include-path Prepend PHP's include_path with given path(s). + -d key[=value] Sets a php.ini value. + + -h|--help Prints this usage information. + --version Prints the version and exits. + +EOT; + } + + /** + * Custom callback for test suite discovery. + */ + protected function handleCustomTestSuite() + { + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/TextUI/ResultPrinter.php b/vendor/phpunit/phpunit/PHPUnit/TextUI/ResultPrinter.php new file mode 100644 index 0000000..0ed0ed2 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/TextUI/ResultPrinter.php @@ -0,0 +1,664 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage TextUI + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Prints the result of a TextUI TestRunner run. + * + * @package PHPUnit + * @subpackage TextUI + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_TextUI_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + const EVENT_TEST_START = 0; + const EVENT_TEST_END = 1; + const EVENT_TESTSUITE_START = 2; + const EVENT_TESTSUITE_END = 3; + + /** + * @var integer + */ + protected $column = 0; + + /** + * @var integer + */ + protected $maxColumn; + + /** + * @var boolean + */ + protected $lastTestFailed = FALSE; + + /** + * @var integer + */ + protected $numAssertions = 0; + + /** + * @var integer + */ + protected $numTests = -1; + + /** + * @var integer + */ + protected $numTestsRun = 0; + + /** + * @var integer + */ + protected $numTestsWidth; + + /** + * @var boolean + */ + protected $colors = FALSE; + + /** + * @var boolean + */ + protected $debug = FALSE; + + /** + * @var boolean + */ + protected $verbose = FALSE; + + /** + * Constructor. + * + * @param mixed $out + * @param boolean $verbose + * @param boolean $colors + * @param boolean $debug + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.0.0 + */ + public function __construct($out = NULL, $verbose = FALSE, $colors = FALSE, $debug = FALSE) + { + parent::__construct($out); + + if (is_bool($verbose)) { + $this->verbose = $verbose; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean'); + } + + if (is_bool($colors)) { + $this->colors = $colors; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'boolean'); + } + + if (is_bool($debug)) { + $this->debug = $debug; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean'); + } + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + public function printResult(PHPUnit_Framework_TestResult $result) + { + $this->printHeader(); + + if ($result->errorCount() > 0) { + $this->printErrors($result); + } + + if ($result->failureCount() > 0) { + if ($result->errorCount() > 0) { + print "\n--\n\n"; + } + + $this->printFailures($result); + } + + if ($this->verbose) { + if ($result->deprecatedFeaturesCount() > 0) { + if ($result->failureCount() > 0) { + print "\n--\n\nDeprecated PHPUnit features are being used"; + } + + foreach ($result->deprecatedFeatures() as $deprecatedFeature) { + $this->write($deprecatedFeature . "\n\n"); + } + } + + if ($result->notImplementedCount() > 0) { + if ($result->failureCount() > 0) { + print "\n--\n\n"; + } + + $this->printIncompletes($result); + } + + if ($result->skippedCount() > 0) { + if ($result->notImplementedCount() > 0) { + print "\n--\n\n"; + } + + $this->printSkipped($result); + } + } + + $this->printFooter($result); + } + + /** + * @param array $defects + * @param integer $count + * @param string $type + */ + protected function printDefects(array $defects, $count, $type) + { + static $called = FALSE; + + if ($count == 0) { + return; + } + + $this->write( + sprintf( + "%sThere %s %d %s%s:\n", + + $called ? "\n" : '', + ($count == 1) ? 'was' : 'were', + $count, + $type, + ($count == 1) ? '' : 's' + ) + ); + + $i = 1; + + foreach ($defects as $defect) { + $this->printDefect($defect, $i++); + } + + $called = TRUE; + } + + /** + * @param PHPUnit_Framework_TestFailure $defect + * @param integer $count + */ + protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count) + { + $this->printDefectHeader($defect, $count); + $this->printDefectTrace($defect); + } + + /** + * @param PHPUnit_Framework_TestFailure $defect + * @param integer $count + */ + protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count) + { + $failedTest = $defect->failedTest(); + + if ($failedTest instanceof PHPUnit_Framework_SelfDescribing) { + $testName = $failedTest->toString(); + } else { + $testName = get_class($failedTest); + } + + $this->write( + sprintf( + "\n%d) %s\n", + + $count, + $testName + ) + ); + } + + /** + * @param PHPUnit_Framework_TestFailure $defect + */ + protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) + { + $this->write( + $defect->getExceptionAsString() . "\n" . + PHPUnit_Util_Filter::getFilteredStacktrace( + $defect->thrownException() + ) + ); + + $e = $defect->thrownException()->getPrevious(); + + while ($e) { + $this->write( + "\nCaused by\n" . + PHPUnit_Framework_TestFailure::exceptionToString($e). "\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e) + ); + + $e = $e->getPrevious(); + } + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printErrors(PHPUnit_Framework_TestResult $result) + { + $this->printDefects($result->errors(), $result->errorCount(), 'error'); + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printFailures(PHPUnit_Framework_TestResult $result) + { + $this->printDefects( + $result->failures(), + $result->failureCount(), + 'failure' + ); + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printIncompletes(PHPUnit_Framework_TestResult $result) + { + $this->printDefects( + $result->notImplemented(), + $result->notImplementedCount(), + 'incomplete test' + ); + } + + /** + * @param PHPUnit_Framework_TestResult $result + * @since Method available since Release 3.0.0 + */ + protected function printSkipped(PHPUnit_Framework_TestResult $result) + { + $this->printDefects( + $result->skipped(), + $result->skippedCount(), + 'skipped test' + ); + } + + protected function printHeader() + { + $this->write("\n\n" . PHP_Timer::resourceUsage() . "\n\n"); + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printFooter(PHPUnit_Framework_TestResult $result) + { + if (count($result) === 0) { + if ($this->colors) { + $this->write("\x1b[30;43m\x1b[2K"); + } + + $this->write( + "No tests executed!\n" + ); + + if ($this->colors) { + $this->write("\x1b[0m\x1b[2K"); + } + } + + else if ($result->wasSuccessful() && + $result->allCompletlyImplemented() && + $result->noneSkipped()) { + if ($this->colors) { + $this->write("\x1b[30;42m\x1b[2K"); + } + + $this->write( + sprintf( + "OK (%d test%s, %d assertion%s)\n", + + count($result), + (count($result) == 1) ? '' : 's', + $this->numAssertions, + ($this->numAssertions == 1) ? '' : 's' + ) + ); + + if ($this->colors) { + $this->write("\x1b[0m\x1b[2K"); + } + } + + else if ((!$result->allCompletlyImplemented() || + !$result->noneSkipped()) && + $result->wasSuccessful()) { + if ($this->colors) { + $this->write( + "\x1b[30;43m\x1b[2KOK, but incomplete or skipped tests!\n" . + "\x1b[0m\x1b[30;43m\x1b[2K" + ); + } else { + $this->write("OK, but incomplete or skipped tests!\n"); + } + + $this->write( + sprintf( + "Tests: %d, Assertions: %d%s%s.\n", + + count($result), + $this->numAssertions, + $this->getCountString( + $result->notImplementedCount(), 'Incomplete' + ), + $this->getCountString( + $result->skippedCount(), 'Skipped' + ) + ) + ); + + if ($this->colors) { + $this->write("\x1b[0m\x1b[2K"); + } + } + + else { + $this->write("\n"); + + if ($this->colors) { + $this->write( + "\x1b[37;41m\x1b[2KFAILURES!\n\x1b[0m\x1b[37;41m\x1b[2K" + ); + } else { + $this->write("FAILURES!\n"); + } + + $this->write( + sprintf( + "Tests: %d, Assertions: %s%s%s%s%s.\n", + + count($result), + $this->numAssertions, + $this->getCountString($result->failureCount(), 'Failures'), + $this->getCountString($result->errorCount(), 'Errors'), + $this->getCountString( + $result->notImplementedCount(), 'Incomplete' + ), + $this->getCountString($result->skippedCount(), 'Skipped') + ) + ); + + if ($this->colors) { + $this->write("\x1b[0m\x1b[2K"); + } + } + + if (!$this->verbose && + $result->deprecatedFeaturesCount() > 0) { + $message = sprintf( + "Warning: Deprecated PHPUnit features are being used %s times!\n" . + "Use --verbose for more information.\n", + $result->deprecatedFeaturesCount() + ); + + if ($this->colors) { + $message = "\x1b[37;41m\x1b[2K" . $message . + "\x1b[0m"; + } + + $this->write("\n" . $message); + } + } + + /** + * @param integer $count + * @param string $name + * @return string + * @since Method available since Release 3.0.0 + */ + protected function getCountString($count, $name) + { + $string = ''; + + if ($count > 0) { + $string = sprintf( + ', %s: %d', + + $name, + $count + ); + } + + return $string; + } + + /** + */ + public function printWaitPrompt() + { + $this->write("\n to continue\n"); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->colors) { + $this->writeProgress("\x1b[31;1mE\x1b[0m"); + } else { + $this->writeProgress('E'); + } + + $this->lastTestFailed = TRUE; + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if ($this->colors) { + $this->writeProgress("\x1b[41;37mF\x1b[0m"); + } else { + $this->writeProgress('F'); + } + + $this->lastTestFailed = TRUE; + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->colors) { + $this->writeProgress("\x1b[33;1mI\x1b[0m"); + } else { + $this->writeProgress('I'); + } + + $this->lastTestFailed = TRUE; + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->colors) { + $this->writeProgress("\x1b[36;1mS\x1b[0m"); + } else { + $this->writeProgress('S'); + } + + $this->lastTestFailed = TRUE; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + if ($this->numTests == -1) { + $this->numTests = count($suite); + $this->numTestsWidth = strlen((string)$this->numTests); + $this->maxColumn = 69 - (2 * $this->numTestsWidth); + } + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if ($this->debug) { + $this->write( + sprintf( + "\nStarting test '%s'.\n", PHPUnit_Util_Test::describe($test) + ) + ); + } + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if (!$this->lastTestFailed) { + $this->writeProgress('.'); + } + + if ($test instanceof PHPUnit_Framework_TestCase) { + $this->numAssertions += $test->getNumAssertions(); + } + + else if ($test instanceof PHPUnit_Extensions_PhptTestCase) { + $this->numAssertions++; + } + + $this->lastTestFailed = FALSE; + + if ($test instanceof PHPUnit_Framework_TestCase) { + if (!$test->hasPerformedExpectationsOnOutput()) { + $this->write($test->getActualOutput()); + } + } + } + + /** + * @param string $progress + */ + protected function writeProgress($progress) + { + $this->write($progress); + $this->column++; + $this->numTestsRun++; + + if ($this->column == $this->maxColumn) { + $this->write( + sprintf( + ' %' . $this->numTestsWidth . 'd / %' . + $this->numTestsWidth . 'd (%3s%%)', + + $this->numTestsRun, + $this->numTests, + floor(($this->numTestsRun / $this->numTests) * 100) + ) + ); + + $this->writeNewLine(); + } + } + + protected function writeNewLine() + { + $this->column = 0; + $this->write("\n"); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php b/vendor/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php new file mode 100644 index 0000000..803392a --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php @@ -0,0 +1,814 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage TextUI + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * A TestRunner for the Command Line Interface (CLI) + * PHP SAPI Module. + * + * @package PHPUnit + * @subpackage TextUI + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_TextUI_TestRunner extends PHPUnit_Runner_BaseTestRunner +{ + const SUCCESS_EXIT = 0; + const FAILURE_EXIT = 1; + const EXCEPTION_EXIT = 2; + + /** + * @var PHP_CodeCoverage_Filter + */ + protected $codeCoverageFilter; + + /** + * @var PHPUnit_Runner_TestSuiteLoader + */ + protected $loader = NULL; + + /** + * @var PHPUnit_TextUI_ResultPrinter + */ + protected $printer = NULL; + + /** + * @var boolean + */ + protected static $versionStringPrinted = FALSE; + + /** + * @param PHPUnit_Runner_TestSuiteLoader $loader + * @param PHP_CodeCoverage_Filter $filter + * @since Method available since Release 3.4.0 + */ + public function __construct(PHPUnit_Runner_TestSuiteLoader $loader = NULL, PHP_CodeCoverage_Filter $filter = NULL) + { + if ($filter === NULL) { + $filter = new PHP_CodeCoverage_Filter; + } + + $this->codeCoverageFilter = $filter; + $this->loader = $loader; + } + + /** + * @param mixed $test + * @param array $arguments + * @throws PHPUnit_Framework_Exception + */ + public static function run($test, array $arguments = array()) + { + if ($test instanceof ReflectionClass) { + $test = new PHPUnit_Framework_TestSuite($test); + } + + if ($test instanceof PHPUnit_Framework_Test) { + $aTestRunner = new PHPUnit_TextUI_TestRunner; + + return $aTestRunner->doRun( + $test, + $arguments + ); + } else { + throw new PHPUnit_Framework_Exception( + 'No test case or test suite found.' + ); + } + } + + /** + * @return PHPUnit_Framework_TestResult + */ + protected function createTestResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * @param PHPUnit_Framework_Test $suite + * @param array $arguments + * @return PHPUnit_Framework_TestResult + */ + public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) + { + $this->handleConfiguration($arguments); + + if (isset($arguments['bootstrap'])) { + $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; + } + + if ($arguments['backupGlobals'] === FALSE) { + $suite->setBackupGlobals(FALSE); + } + + if ($arguments['backupStaticAttributes'] === TRUE) { + $suite->setBackupStaticAttributes(TRUE); + } + + if (is_integer($arguments['repeat'])) { + $suite = new PHPUnit_Extensions_RepeatedTest( + $suite, + $arguments['repeat'], + $arguments['filter'], + $arguments['groups'], + $arguments['excludeGroups'], + $arguments['processIsolation'] + ); + } + + $result = $this->createTestResult(); + + if (!$arguments['convertErrorsToExceptions']) { + $result->convertErrorsToExceptions(FALSE); + } + + if (!$arguments['convertNoticesToExceptions']) { + PHPUnit_Framework_Error_Notice::$enabled = FALSE; + } + + if (!$arguments['convertWarningsToExceptions']) { + PHPUnit_Framework_Error_Warning::$enabled = FALSE; + } + + if ($arguments['stopOnError']) { + $result->stopOnError(TRUE); + } + + if ($arguments['stopOnFailure']) { + $result->stopOnFailure(TRUE); + } + + if ($arguments['stopOnIncomplete']) { + $result->stopOnIncomplete(TRUE); + } + + if ($arguments['stopOnSkipped']) { + $result->stopOnSkipped(TRUE); + } + + if ($this->printer === NULL) { + if (isset($arguments['printer']) && + $arguments['printer'] instanceof PHPUnit_Util_Printer) { + $this->printer = $arguments['printer']; + } else { + $this->printer = new PHPUnit_TextUI_ResultPrinter( + NULL, + $arguments['verbose'], + $arguments['colors'], + $arguments['debug'] + ); + } + } + + if (!$this->printer instanceof PHPUnit_Util_Log_TAP && + !self::$versionStringPrinted) { + $this->printer->write( + PHPUnit_Runner_Version::getVersionString() . "\n\n" + ); + + if (isset($arguments['configuration'])) { + $this->printer->write( + sprintf( + "Configuration read from %s\n\n", + $arguments['configuration']->getFilename() + ) + ); + } + } + + foreach ($arguments['listeners'] as $listener) { + $result->addListener($listener); + } + + $result->addListener($this->printer); + + if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) { + $result->addListener(new PHPUnit_Util_DeprecatedFeature_Logger); + } + + if (isset($arguments['testdoxHTMLFile'])) { + $result->addListener( + new PHPUnit_Util_TestDox_ResultPrinter_HTML( + $arguments['testdoxHTMLFile'] + ) + ); + } + + if (isset($arguments['testdoxTextFile'])) { + $result->addListener( + new PHPUnit_Util_TestDox_ResultPrinter_Text( + $arguments['testdoxTextFile'] + ) + ); + } + + $codeCoverageReports = 0; + + if (extension_loaded('xdebug')) { + if (isset($arguments['coverageClover'])) { + $codeCoverageReports++; + } + + if (isset($arguments['reportDirectory'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coveragePHP'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageText'])) { + $codeCoverageReports++; + } + } + + if ($codeCoverageReports > 0) { + $codeCoverage = new PHP_CodeCoverage( + NULL, $this->codeCoverageFilter + ); + + $codeCoverage->setAddUncoveredFilesFromWhitelist( + $arguments['addUncoveredFilesFromWhitelist'] + ); + + $codeCoverage->setProcessUncoveredFilesFromWhitelist( + $arguments['processUncoveredFilesFromWhitelist'] + ); + + if (isset($arguments['forceCoversAnnotation'])) { + $codeCoverage->setForceCoversAnnotation( + $arguments['forceCoversAnnotation'] + ); + } + + if (isset($arguments['mapTestClassNameToCoveredClassName'])) { + $codeCoverage->setMapTestClassNameToCoveredClassName( + $arguments['mapTestClassNameToCoveredClassName'] + ); + } + + $result->setCodeCoverage($codeCoverage); + } + + if ($codeCoverageReports > 1) { + if (isset($arguments['cacheTokens'])) { + $codeCoverage->setCacheTokens($arguments['cacheTokens']); + } + } + + if (isset($arguments['jsonLogfile'])) { + $result->addListener( + new PHPUnit_Util_Log_JSON($arguments['jsonLogfile']) + ); + } + + if (isset($arguments['tapLogfile'])) { + $result->addListener( + new PHPUnit_Util_Log_TAP($arguments['tapLogfile']) + ); + } + + if (isset($arguments['junitLogfile'])) { + $result->addListener( + new PHPUnit_Util_Log_JUnit( + $arguments['junitLogfile'], $arguments['logIncompleteSkipped'] + ) + ); + } + + if ($arguments['strict']) { + $result->strictMode(TRUE); + + $result->setTimeoutForSmallTests( + $arguments['timeoutForSmallTests'] + ); + + $result->setTimeoutForMediumTests( + $arguments['timeoutForMediumTests'] + ); + + $result->setTimeoutForLargeTests( + $arguments['timeoutForLargeTests'] + ); + } + + $suite->run( + $result, + $arguments['filter'], + $arguments['groups'], + $arguments['excludeGroups'], + $arguments['processIsolation'] + ); + + unset($suite); + $result->flushListeners(); + + if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) { + $this->printer->printResult($result); + } + + if (isset($codeCoverage)) { + if (isset($arguments['coverageClover'])) { + $this->printer->write( + "\nGenerating code coverage report in Clover XML format ..." + ); + + $writer = new PHP_CodeCoverage_Report_Clover; + $writer->process($codeCoverage, $arguments['coverageClover']); + + $this->printer->write(" done\n"); + unset($writer); + } + + if (isset($arguments['reportDirectory'])) { + $this->printer->write( + "\nGenerating code coverage report in HTML format ..." + ); + + $writer = new PHP_CodeCoverage_Report_HTML( + $arguments['reportCharset'], + $arguments['reportHighlight'], + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + sprintf( + ' and PHPUnit %s', + PHPUnit_Runner_Version::id() + ) + ); + + $writer->process($codeCoverage, $arguments['reportDirectory']); + + $this->printer->write(" done\n"); + unset($writer); + } + + if (isset($arguments['coveragePHP'])) { + $this->printer->write( + "\nGenerating code coverage report in PHP format ..." + ); + + $writer = new PHP_CodeCoverage_Report_PHP; + $writer->process($codeCoverage, $arguments['coveragePHP']); + + $this->printer->write(" done\n"); + unset($writer); + } + + if (isset($arguments['coverageText'])) { + if ($arguments['coverageText'] == 'php://stdout') { + $outputStream = $this->printer; + $colors = (bool)$arguments['colors']; + } else { + $outputStream = new PHPUnit_Util_Printer($arguments['coverageText']); + $colors = FALSE; + } + + $writer = new PHP_CodeCoverage_Report_Text( + $outputStream, + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + $arguments['coverageTextShowUncoveredFiles'] + ); + + $writer->process($codeCoverage, $colors); + } + } + + return $result; + } + + /** + * @param PHPUnit_TextUI_ResultPrinter $resultPrinter + */ + public function setPrinter(PHPUnit_TextUI_ResultPrinter $resultPrinter) + { + $this->printer = $resultPrinter; + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + * + * @param string $message + */ + protected function runFailed($message) + { + self::printVersionString(); + self::write($message . PHP_EOL); + exit(self::FAILURE_EXIT); + } + + /** + * @param string $buffer + * @since Method available since Release 3.1.0 + */ + protected static function write($buffer) + { + if (PHP_SAPI != 'cli') { + $buffer = htmlspecialchars($buffer); + } + + print $buffer; + } + + /** + * Returns the loader to be used. + * + * @return PHPUnit_Runner_TestSuiteLoader + * @since Method available since Release 2.2.0 + */ + public function getLoader() + { + if ($this->loader === NULL) { + $this->loader = new PHPUnit_Runner_StandardTestSuiteLoader; + } + + return $this->loader; + } + + /** + */ + public static function showError($message) + { + self::printVersionString(); + self::write($message . "\n"); + + exit(self::FAILURE_EXIT); + } + + /** + */ + public static function printVersionString() + { + if (!self::$versionStringPrinted) { + self::write(PHPUnit_Runner_Version::getVersionString() . "\n\n"); + self::$versionStringPrinted = TRUE; + } + } + + /** + * @param array $arguments + * @since Method available since Release 3.2.1 + */ + protected function handleConfiguration(array &$arguments) + { + if (isset($arguments['configuration']) && + !$arguments['configuration'] instanceof PHPUnit_Util_Configuration) { + $arguments['configuration'] = PHPUnit_Util_Configuration::getInstance( + $arguments['configuration'] + ); + } + + $arguments['debug'] = isset($arguments['debug']) ? $arguments['debug'] : FALSE; + $arguments['filter'] = isset($arguments['filter']) ? $arguments['filter'] : FALSE; + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + + if (isset($arguments['configuration'])) { + $arguments['configuration']->handlePHPConfiguration(); + + $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration(); + + if (isset($phpunitConfiguration['backupGlobals']) && + !isset($arguments['backupGlobals'])) { + $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals']; + } + + if (isset($phpunitConfiguration['backupStaticAttributes']) && + !isset($arguments['backupStaticAttributes'])) { + $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes']; + } + + if (isset($phpunitConfiguration['bootstrap']) && + !isset($arguments['bootstrap'])) { + $arguments['bootstrap'] = $phpunitConfiguration['bootstrap']; + } + + if (isset($phpunitConfiguration['cacheTokens']) && + !isset($arguments['cacheTokens'])) { + $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; + } + + if (isset($phpunitConfiguration['colors']) && + !isset($arguments['colors'])) { + $arguments['colors'] = $phpunitConfiguration['colors']; + } + + if (isset($phpunitConfiguration['convertErrorsToExceptions']) && + !isset($arguments['convertErrorsToExceptions'])) { + $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions']; + } + + if (isset($phpunitConfiguration['convertNoticesToExceptions']) && + !isset($arguments['convertNoticesToExceptions'])) { + $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions']; + } + + if (isset($phpunitConfiguration['convertWarningsToExceptions']) && + !isset($arguments['convertWarningsToExceptions'])) { + $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions']; + } + + if (isset($phpunitConfiguration['processIsolation']) && + !isset($arguments['processIsolation'])) { + $arguments['processIsolation'] = $phpunitConfiguration['processIsolation']; + } + + if (isset($phpunitConfiguration['stopOnFailure']) && + !isset($arguments['stopOnFailure'])) { + $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure']; + } + + if (isset($phpunitConfiguration['timeoutForSmallTests']) && + !isset($arguments['timeoutForSmallTests'])) { + $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests']; + } + + if (isset($phpunitConfiguration['timeoutForMediumTests']) && + !isset($arguments['timeoutForMediumTests'])) { + $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests']; + } + + if (isset($phpunitConfiguration['timeoutForLargeTests']) && + !isset($arguments['timeoutForLargeTests'])) { + $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests']; + } + + if (isset($phpunitConfiguration['strict']) && + !isset($arguments['strict'])) { + $arguments['strict'] = $phpunitConfiguration['strict']; + } + + if (isset($phpunitConfiguration['verbose']) && + !isset($arguments['verbose'])) { + $arguments['verbose'] = $phpunitConfiguration['verbose']; + } + + if (isset($phpunitConfiguration['forceCoversAnnotation']) && + !isset($arguments['forceCoversAnnotation'])) { + $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation']; + } + + if (isset($phpunitConfiguration['mapTestClassNameToCoveredClassName']) && + !isset($arguments['mapTestClassNameToCoveredClassName'])) { + $arguments['mapTestClassNameToCoveredClassName'] = $phpunitConfiguration['mapTestClassNameToCoveredClassName']; + } + + $groupCliArgs = array(); + if (!empty($arguments['groups'])) { + $groupCliArgs = $arguments['groups']; + } + + $groupConfiguration = $arguments['configuration']->getGroupConfiguration(); + + if (!empty($groupConfiguration['include']) && + !isset($arguments['groups'])) { + $arguments['groups'] = $groupConfiguration['include']; + } + + if (!empty($groupConfiguration['exclude']) && + !isset($arguments['excludeGroups'])) { + $arguments['excludeGroups'] = array_diff($groupConfiguration['exclude'], $groupCliArgs); + } + + foreach ($arguments['configuration']->getListenerConfiguration() as $listener) { + if (!class_exists($listener['class'], FALSE) && + $listener['file'] !== '') { + require_once $listener['file']; + } + + if (class_exists($listener['class'])) { + if (count($listener['arguments']) == 0) { + $listener = new $listener['class']; + } else { + $listenerClass = new ReflectionClass( + $listener['class'] + ); + $listener = $listenerClass->newInstanceArgs( + $listener['arguments'] + ); + } + + if ($listener instanceof PHPUnit_Framework_TestListener) { + $arguments['listeners'][] = $listener; + } + } + } + + $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration(); + + if (isset($loggingConfiguration['coverage-html']) && + !isset($arguments['reportDirectory'])) { + if (isset($loggingConfiguration['charset']) && + !isset($arguments['reportCharset'])) { + $arguments['reportCharset'] = $loggingConfiguration['charset']; + } + + if (isset($loggingConfiguration['highlight']) && + !isset($arguments['reportHighlight'])) { + $arguments['reportHighlight'] = $loggingConfiguration['highlight']; + } + + if (isset($loggingConfiguration['lowUpperBound']) && + !isset($arguments['reportLowUpperBound'])) { + $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound']; + } + + if (isset($loggingConfiguration['highLowerBound']) && + !isset($arguments['reportHighLowerBound'])) { + $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound']; + } + + $arguments['reportDirectory'] = $loggingConfiguration['coverage-html']; + } + + if (isset($loggingConfiguration['coverage-clover']) && + !isset($arguments['coverageClover'])) { + $arguments['coverageClover'] = $loggingConfiguration['coverage-clover']; + } + + if (isset($loggingConfiguration['coverage-php']) && + !isset($arguments['coveragePHP'])) { + $arguments['coveragePHP'] = $loggingConfiguration['coverage-php']; + } + + if (isset($loggingConfiguration['coverage-text']) && + !isset($arguments['coverageText'])) { + $arguments['coverageText'] = $loggingConfiguration['coverage-text']; + if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) { + $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles']; + } else { + $arguments['coverageTextShowUncoveredFiles'] = FALSE; + } + } + + if (isset($loggingConfiguration['json']) && + !isset($arguments['jsonLogfile'])) { + $arguments['jsonLogfile'] = $loggingConfiguration['json']; + } + + if (isset($loggingConfiguration['plain'])) { + $arguments['listeners'][] = new PHPUnit_TextUI_ResultPrinter( + $loggingConfiguration['plain'], TRUE + ); + } + + if (isset($loggingConfiguration['tap']) && + !isset($arguments['tapLogfile'])) { + $arguments['tapLogfile'] = $loggingConfiguration['tap']; + } + + if (isset($loggingConfiguration['junit']) && + !isset($arguments['junitLogfile'])) { + $arguments['junitLogfile'] = $loggingConfiguration['junit']; + + if (isset($loggingConfiguration['logIncompleteSkipped']) && + !isset($arguments['logIncompleteSkipped'])) { + $arguments['logIncompleteSkipped'] = $loggingConfiguration['logIncompleteSkipped']; + } + } + + if (isset($loggingConfiguration['testdox-html']) && + !isset($arguments['testdoxHTMLFile'])) { + $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html']; + } + + if (isset($loggingConfiguration['testdox-text']) && + !isset($arguments['testdoxTextFile'])) { + $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text']; + } + + if ((isset($arguments['coverageClover']) || + isset($arguments['reportDirectory']) || + isset($arguments['coveragePHP']) || + isset($arguments['coverageText'])) && + extension_loaded('xdebug')) { + + $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); + $arguments['addUncoveredFilesFromWhitelist'] = $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']; + $arguments['processUncoveredFilesFromWhitelist'] = $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']; + + foreach ($filterConfiguration['blacklist']['include']['directory'] as $dir) { + $this->codeCoverageFilter->addDirectoryToBlacklist( + $dir['path'], $dir['suffix'], $dir['prefix'], $dir['group'] + ); + } + + foreach ($filterConfiguration['blacklist']['include']['file'] as $file) { + $this->codeCoverageFilter->addFileToBlacklist($file); + } + + foreach ($filterConfiguration['blacklist']['exclude']['directory'] as $dir) { + $this->codeCoverageFilter->removeDirectoryFromBlacklist( + $dir['path'], $dir['suffix'], $dir['prefix'], $dir['group'] + ); + } + + foreach ($filterConfiguration['blacklist']['exclude']['file'] as $file) { + $this->codeCoverageFilter->removeFileFromBlacklist($file); + } + + foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) { + $this->codeCoverageFilter->addDirectoryToWhitelist( + $dir['path'], $dir['suffix'], $dir['prefix'] + ); + } + + foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { + $this->codeCoverageFilter->addFileToWhitelist($file); + } + + foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { + $this->codeCoverageFilter->removeDirectoryFromWhitelist( + $dir['path'], $dir['suffix'], $dir['prefix'] + ); + } + + foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { + $this->codeCoverageFilter->removeFileFromWhitelist($file); + } + } + } + + $arguments['addUncoveredFilesFromWhitelist'] = isset($arguments['addUncoveredFilesFromWhitelist']) ? $arguments['addUncoveredFilesFromWhitelist'] : TRUE; + $arguments['processUncoveredFilesFromWhitelist'] = isset($arguments['processUncoveredFilesFromWhitelist']) ? $arguments['processUncoveredFilesFromWhitelist'] : FALSE; + $arguments['backupGlobals'] = isset($arguments['backupGlobals']) ? $arguments['backupGlobals'] : NULL; + $arguments['backupStaticAttributes'] = isset($arguments['backupStaticAttributes']) ? $arguments['backupStaticAttributes'] : NULL; + $arguments['cacheTokens'] = isset($arguments['cacheTokens']) ? $arguments['cacheTokens'] : FALSE; + $arguments['colors'] = isset($arguments['colors']) ? $arguments['colors'] : FALSE; + $arguments['convertErrorsToExceptions'] = isset($arguments['convertErrorsToExceptions']) ? $arguments['convertErrorsToExceptions'] : TRUE; + $arguments['convertNoticesToExceptions'] = isset($arguments['convertNoticesToExceptions']) ? $arguments['convertNoticesToExceptions'] : TRUE; + $arguments['convertWarningsToExceptions'] = isset($arguments['convertWarningsToExceptions']) ? $arguments['convertWarningsToExceptions'] : TRUE; + $arguments['excludeGroups'] = isset($arguments['excludeGroups']) ? $arguments['excludeGroups'] : array(); + $arguments['groups'] = isset($arguments['groups']) ? $arguments['groups'] : array(); + $arguments['logIncompleteSkipped'] = isset($arguments['logIncompleteSkipped']) ? $arguments['logIncompleteSkipped'] : FALSE; + $arguments['processIsolation'] = isset($arguments['processIsolation']) ? $arguments['processIsolation'] : FALSE; + $arguments['repeat'] = isset($arguments['repeat']) ? $arguments['repeat'] : FALSE; + $arguments['reportCharset'] = isset($arguments['reportCharset']) ? $arguments['reportCharset'] : 'UTF-8'; + $arguments['reportHighlight'] = isset($arguments['reportHighlight']) ? $arguments['reportHighlight'] : FALSE; + $arguments['reportHighLowerBound'] = isset($arguments['reportHighLowerBound']) ? $arguments['reportHighLowerBound'] : 70; + $arguments['reportLowUpperBound'] = isset($arguments['reportLowUpperBound']) ? $arguments['reportLowUpperBound'] : 35; + $arguments['stopOnError'] = isset($arguments['stopOnError']) ? $arguments['stopOnError'] : FALSE; + $arguments['stopOnFailure'] = isset($arguments['stopOnFailure']) ? $arguments['stopOnFailure'] : FALSE; + $arguments['stopOnIncomplete'] = isset($arguments['stopOnIncomplete']) ? $arguments['stopOnIncomplete'] : FALSE; + $arguments['stopOnSkipped'] = isset($arguments['stopOnSkipped']) ? $arguments['stopOnSkipped'] : FALSE; + $arguments['timeoutForSmallTests'] = isset($arguments['timeoutForSmallTests']) ? $arguments['timeoutForSmallTests'] : 1; + $arguments['timeoutForMediumTests'] = isset($arguments['timeoutForMediumTests']) ? $arguments['timeoutForMediumTests'] : 10; + $arguments['timeoutForLargeTests'] = isset($arguments['timeoutForLargeTests']) ? $arguments['timeoutForLargeTests'] : 60; + $arguments['strict'] = isset($arguments['strict']) ? $arguments['strict'] : FALSE; + $arguments['verbose'] = isset($arguments['verbose']) ? $arguments['verbose'] : FALSE; + + if ($arguments['filter'] !== FALSE && + preg_match('/^[a-zA-Z0-9_]/', $arguments['filter'])) { + // Escape delimiters in regular expression. Do NOT use preg_quote, + // to keep magic characters. + $arguments['filter'] = '/' . str_replace( + '/', '\\/', $arguments['filter'] + ) . '/'; + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Class.php b/vendor/phpunit/phpunit/PHPUnit/Util/Class.php new file mode 100644 index 0000000..85ac032 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Class.php @@ -0,0 +1,363 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.0 + */ + +/** + * Class helpers. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Util_Class +{ + protected static $buffer = array(); + + /** + * Starts the collection of loaded classes. + * + */ + public static function collectStart() + { + self::$buffer = get_declared_classes(); + } + + /** + * Stops the collection of loaded classes and + * returns the names of the loaded classes. + * + * @return array + */ + public static function collectEnd() + { + return array_values( + array_diff(get_declared_classes(), self::$buffer) + ); + } + + /** + * Returns the class hierarchy for a given class. + * + * @param string $className + * @param boolean $asReflectionObjects + * @return array + */ + public static function getHierarchy($className, $asReflectionObjects = FALSE) + { + if ($asReflectionObjects) { + $classes = array(new ReflectionClass($className)); + } else { + $classes = array($className); + } + + $done = FALSE; + + while (!$done) { + if ($asReflectionObjects) { + $class = new ReflectionClass( + $classes[count($classes)-1]->getName() + ); + } else { + $class = new ReflectionClass($classes[count($classes)-1]); + } + + $parent = $class->getParentClass(); + + if ($parent !== FALSE) { + if ($asReflectionObjects) { + $classes[] = $parent; + } else { + $classes[] = $parent->getName(); + } + } else { + $done = TRUE; + } + } + + return $classes; + } + + /** + * Returns the parameters of a function or method. + * + * @param ReflectionFunction|ReflectionMethod $method + * @param boolean $forCall + * @return string + * @since Method available since Release 3.2.0 + */ + public static function getMethodParameters($method, $forCall = FALSE) + { + $parameters = array(); + + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + + $default = ''; + $reference = ''; + $typeHint = ''; + + if (!$forCall) { + if ($parameter->isArray()) { + $typeHint = 'array '; + } + + else if (version_compare(PHP_VERSION, '5.4', '>') && + $parameter->isCallable()) { + $typeHint = 'callable '; + } + + else { + try { + $class = $parameter->getClass(); + } + + catch (ReflectionException $e) { + $class = FALSE; + } + + if ($class) { + $typeHint = $class->getName() . ' '; + } + } + + if ($parameter->isDefaultValueAvailable()) { + $value = $parameter->getDefaultValue(); + $default = ' = ' . var_export($value, TRUE); + } + + else if ($parameter->isOptional()) { + $default = ' = null'; + } + + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + } + + $parameters[] = $typeHint . $reference . $name . $default; + } + + return join(', ', $parameters); + } + + /** + * Returns the package information of a user-defined class. + * + * @param string $className + * @param string $docComment + * @return array + */ + public static function getPackageInformation($className, $docComment) + { + $result = array( + 'namespace' => '', + 'fullPackage' => '', + 'category' => '', + 'package' => '', + 'subpackage' => '' + ); + + if (strpos($className, '\\') !== FALSE) { + $result['namespace'] = self::arrayToName( + explode('\\', $className) + ); + } + + if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['category'] = $matches[1]; + } + + if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['package'] = $matches[1]; + $result['fullPackage'] = $matches[1]; + } + + if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['subpackage'] = $matches[1]; + $result['fullPackage'] .= '.' . $matches[1]; + } + + if (empty($result['fullPackage'])) { + $result['fullPackage'] = self::arrayToName( + explode('_', str_replace('\\', '_', $className)), '.' + ); + } + + return $result; + } + + /** + * Returns the value of a static attribute. + * This also works for attributes that are declared protected or private. + * + * @param string $className + * @param string $attributeName + * @return mixed + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.4.0 + */ + public static function getStaticAttribute($className, $attributeName) + { + if (!is_string($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!class_exists($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class name'); + } + + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $class = new ReflectionClass($className); + + while ($class) { + $attributes = $class->getStaticProperties(); + + if (array_key_exists($attributeName, $attributes)) { + return $attributes[$attributeName]; + } + + $class = $class->getParentClass(); + } + + throw new PHPUnit_Framework_Exception( + sprintf( + 'Attribute "%s" not found in class.', + + $attributeName + ) + ); + } + + /** + * Returns the value of an object's attribute. + * This also works for attributes that are declared protected or private. + * + * @param object $object + * @param string $attributeName + * @return mixed + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.4.0 + */ + public static function getObjectAttribute($object, $attributeName) + { + if (!is_object($object)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'object'); + } + + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + try { + $attribute = new ReflectionProperty($object, $attributeName); + } + + catch (ReflectionException $e) { + $reflector = new ReflectionObject($object); + + while ($reflector = $reflector->getParentClass()) { + try { + $attribute = $reflector->getProperty($attributeName); + break; + } + + catch(ReflectionException $e) { + } + } + } + + if (isset($attribute)) { + if (!$attribute || $attribute->isPublic()) { + return $object->$attributeName; + } + $attribute->setAccessible(TRUE); + $value = $attribute->getValue($object); + $attribute->setAccessible(FALSE); + + return $value; + } + + throw new PHPUnit_Framework_Exception( + sprintf( + 'Attribute "%s" not found in object.', + $attributeName + ) + ); + } + + /** + * Returns the package information of a user-defined class. + * + * @param array $parts + * @param string $join + * @return string + * @since Method available since Release 3.2.12 + */ + protected static function arrayToName(array $parts, $join = '\\') + { + $result = ''; + + if (count($parts) > 1) { + array_pop($parts); + + $result = join($join, $parts); + } + + return $result; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Configuration.php b/vendor/phpunit/phpunit/PHPUnit/Util/Configuration.php new file mode 100644 index 0000000..bd82673 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Configuration.php @@ -0,0 +1,1026 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.2.0 + */ + +/** + * Wrapper for the PHPUnit XML configuration file. + * + * Example XML configuration file: + * + * + * + * + * + * + * /path/to/files + * /path/to/MyTest.php + * /path/to/files/exclude + * + * + * + * + * + * name + * + * + * name + * + * + * + * + * + * /path/to/files + * /path/to/file + * + * /path/to/files + * /path/to/file + * + * + * + * /path/to/files + * /path/to/file + * + * /path/to/files + * /path/to/file + * + * + * + * + * + * + * + * + * + * Sebastian + * + * + * 22 + * April + * 19.78 + * + * + * MyRelativeFile.php + * MyRelativeDir + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * . + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.2.0 + */ +class PHPUnit_Util_Configuration +{ + private static $instances = array(); + + protected $document; + protected $xpath; + protected $filename; + + /** + * Loads a PHPUnit configuration file. + * + * @param string $filename + */ + protected function __construct($filename) + { + $this->filename = $filename; + $this->document = PHPUnit_Util_XML::loadFile($filename, FALSE, TRUE); + $this->xpath = new DOMXPath($this->document); + } + + /** + * @since Method available since Release 3.4.0 + */ + private final function __clone() + { + } + + /** + * Returns a PHPUnit configuration object. + * + * @param string $filename + * @return PHPUnit_Util_Configuration + * @since Method available since Release 3.4.0 + */ + public static function getInstance($filename) + { + $realpath = realpath($filename); + + if ($realpath === FALSE) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Could not read "%s".', + $filename + ) + ); + } + + if (!isset(self::$instances[$realpath])) { + self::$instances[$realpath] = new PHPUnit_Util_Configuration($realpath); + } + + return self::$instances[$realpath]; + } + + /** + * Returns the realpath to the configuration file. + * + * @return string + * @since Method available since Release 3.6.0 + */ + public function getFilename() + { + return $this->filename; + } + + /** + * Returns the configuration for SUT filtering. + * + * @return array + * @since Method available since Release 3.2.1 + */ + public function getFilterConfiguration() + { + $addUncoveredFilesFromWhitelist = TRUE; + $processUncoveredFilesFromWhitelist = FALSE; + + $tmp = $this->xpath->query('filter/whitelist'); + + if ($tmp->length == 1) { + if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { + $addUncoveredFilesFromWhitelist = $this->getBoolean( + (string)$tmp->item(0)->getAttribute( + 'addUncoveredFilesFromWhitelist' + ), + TRUE + ); + } + + if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { + $processUncoveredFilesFromWhitelist = $this->getBoolean( + (string)$tmp->item(0)->getAttribute( + 'processUncoveredFilesFromWhitelist' + ), + FALSE + ); + } + } + + return array( + 'blacklist' => array( + 'include' => array( + 'directory' => $this->readFilterDirectories( + 'filter/blacklist/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/blacklist/file' + ) + ), + 'exclude' => array( + 'directory' => $this->readFilterDirectories( + 'filter/blacklist/exclude/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/blacklist/exclude/file' + ) + ) + ), + 'whitelist' => array( + 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, + 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, + 'include' => array( + 'directory' => $this->readFilterDirectories( + 'filter/whitelist/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/whitelist/file' + ) + ), + 'exclude' => array( + 'directory' => $this->readFilterDirectories( + 'filter/whitelist/exclude/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/whitelist/exclude/file' + ) + ) + ) + ); + } + + /** + * Returns the configuration for groups. + * + * @return array + * @since Method available since Release 3.2.1 + */ + public function getGroupConfiguration() + { + $groups = array( + 'include' => array(), + 'exclude' => array() + ); + + foreach ($this->xpath->query('groups/include/group') as $group) { + $groups['include'][] = (string)$group->nodeValue; + } + + foreach ($this->xpath->query('groups/exclude/group') as $group) { + $groups['exclude'][] = (string)$group->nodeValue; + } + + return $groups; + } + + /** + * Returns the configuration for listeners. + * + * @return array + * @since Method available since Release 3.4.0 + */ + public function getListenerConfiguration() + { + $result = array(); + + foreach ($this->xpath->query('listeners/listener') as $listener) { + $class = (string)$listener->getAttribute('class'); + $file = ''; + $arguments = array(); + + if ($listener->hasAttribute('file')) { + $file = $this->toAbsolutePath( + (string)$listener->getAttribute('file'), TRUE + ); + } + + foreach ($listener->childNodes as $node) { + if ($node instanceof DOMElement && $node->tagName == 'arguments') { + foreach ($node->childNodes as $argument) { + if ($argument instanceof DOMElement) { + if ($argument->tagName == 'file' || + $argument->tagName == 'directory') { + $arguments[] = $this->toAbsolutePath((string)$argument->nodeValue); + } else { + $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument); + } + } + } + } + } + + $result[] = array( + 'class' => $class, + 'file' => $file, + 'arguments' => $arguments + ); + } + + return $result; + } + + /** + * Returns the logging configuration. + * + * @return array + */ + public function getLoggingConfiguration() + { + $result = array(); + + foreach ($this->xpath->query('logging/log') as $log) { + $type = (string)$log->getAttribute('type'); + + $target = $this->toAbsolutePath( + (string)$log->getAttribute('target') + ); + + if ($type == 'coverage-html') { + if ($log->hasAttribute('title')) { + $result['title'] = (string)$log->getAttribute('title'); + } + + if ($log->hasAttribute('charset')) { + $result['charset'] = (string)$log->getAttribute('charset'); + } + + if ($log->hasAttribute('lowUpperBound')) { + $result['lowUpperBound'] = (string)$log->getAttribute('lowUpperBound'); + } + + if ($log->hasAttribute('highLowerBound')) { + $result['highLowerBound'] = (string)$log->getAttribute('highLowerBound'); + } + + if ($log->hasAttribute('highlight')) { + $result['highlight'] = $this->getBoolean( + (string)$log->getAttribute('highlight'), + FALSE + ); + } + } + + else if ($type == 'junit') { + if ($log->hasAttribute('logIncompleteSkipped')) { + $result['logIncompleteSkipped'] = $this->getBoolean( + (string)$log->getAttribute('logIncompleteSkipped'), + FALSE + ); + } + } + + else if ($type == 'coverage-text') { + if ($log->hasAttribute('showUncoveredFiles')) { + $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( + (string)$log->getAttribute('showUncoveredFiles'), + FALSE + ); + } + } + + $result[$type] = $target; + } + + return $result; + } + + /** + * Returns the PHP configuration. + * + * @return array + * @since Method available since Release 3.2.1 + */ + public function getPHPConfiguration() + { + $result = array( + 'include_path' => array(), + 'ini' => array(), + 'const' => array(), + 'var' => array(), + 'env' => array(), + 'post' => array(), + 'get' => array(), + 'cookie' => array(), + 'server' => array(), + 'files' => array(), + 'request' => array() + ); + + foreach ($this->xpath->query('php/includePath') as $includePath) { + $path = (string)$includePath->nodeValue; + + $result['include_path'][] = $this->toAbsolutePath($path); + } + + foreach ($this->xpath->query('php/ini') as $ini) { + $name = (string)$ini->getAttribute('name'); + $value = (string)$ini->getAttribute('value'); + + $result['ini'][$name] = $value; + } + + foreach ($this->xpath->query('php/const') as $const) { + $name = (string)$const->getAttribute('name'); + $value = (string)$const->getAttribute('value'); + + $result['const'][$name] = $this->getBoolean($value, $value); + } + + foreach (array('var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) { + foreach ($this->xpath->query('php/' . $array) as $var) { + $name = (string)$var->getAttribute('name'); + $value = (string)$var->getAttribute('value'); + + $result[$array][$name] = $this->getBoolean($value, $value); + } + } + + return $result; + } + + /** + * Handles the PHP configuration. + * + * @since Method available since Release 3.2.20 + */ + public function handlePHPConfiguration() + { + $configuration = $this->getPHPConfiguration(); + + if (! empty($configuration['include_path'])) { + ini_set( + 'include_path', + implode(PATH_SEPARATOR, $configuration['include_path']) . + PATH_SEPARATOR . + ini_get('include_path') + ); + } + + foreach ($configuration['ini'] as $name => $value) { + if (defined($value)) { + $value = constant($value); + } + + ini_set($name, $value); + } + + foreach ($configuration['const'] as $name => $value) { + if (!defined($name)) { + define($name, $value); + } + } + + foreach (array('var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) { + if ($array == 'var') { + $target = &$GLOBALS; + } else { + $target = &$GLOBALS['_' . strtoupper($array)]; + } + + foreach ($configuration[$array] as $name => $value) { + $target[$name] = $value; + } + } + + foreach ($configuration['env'] as $name => $value) { + putenv("$name=$value"); + } + } + + /** + * Returns the PHPUnit configuration. + * + * @return array + * @since Method available since Release 3.2.14 + */ + public function getPHPUnitConfiguration() + { + $result = array(); + $root = $this->document->documentElement; + + if ($root->hasAttribute('cacheTokens')) { + $result['cacheTokens'] = $this->getBoolean( + (string)$root->getAttribute('cacheTokens'), FALSE + ); + } + + if ($root->hasAttribute('colors')) { + $result['colors'] = $this->getBoolean( + (string)$root->getAttribute('colors'), FALSE + ); + } + + if ($root->hasAttribute('backupGlobals')) { + $result['backupGlobals'] = $this->getBoolean( + (string)$root->getAttribute('backupGlobals'), TRUE + ); + } + + if ($root->hasAttribute('backupStaticAttributes')) { + $result['backupStaticAttributes'] = $this->getBoolean( + (string)$root->getAttribute('backupStaticAttributes'), FALSE + ); + } + + if ($root->hasAttribute('bootstrap')) { + $result['bootstrap'] = $this->toAbsolutePath( + (string)$root->getAttribute('bootstrap') + ); + } + + if ($root->hasAttribute('convertErrorsToExceptions')) { + $result['convertErrorsToExceptions'] = $this->getBoolean( + (string)$root->getAttribute('convertErrorsToExceptions'), TRUE + ); + } + + if ($root->hasAttribute('convertNoticesToExceptions')) { + $result['convertNoticesToExceptions'] = $this->getBoolean( + (string)$root->getAttribute('convertNoticesToExceptions'), TRUE + ); + } + + if ($root->hasAttribute('convertWarningsToExceptions')) { + $result['convertWarningsToExceptions'] = $this->getBoolean( + (string)$root->getAttribute('convertWarningsToExceptions'), TRUE + ); + } + + if ($root->hasAttribute('forceCoversAnnotation')) { + $result['forceCoversAnnotation'] = $this->getBoolean( + (string)$root->getAttribute('forceCoversAnnotation'), FALSE + ); + } + + if ($root->hasAttribute('mapTestClassNameToCoveredClassName')) { + $result['mapTestClassNameToCoveredClassName'] = $this->getBoolean( + (string)$root->getAttribute('mapTestClassNameToCoveredClassName'), + FALSE + ); + } + + if ($root->hasAttribute('processIsolation')) { + $result['processIsolation'] = $this->getBoolean( + (string)$root->getAttribute('processIsolation'), FALSE + ); + } + + if ($root->hasAttribute('stopOnError')) { + $result['stopOnError'] = $this->getBoolean( + (string)$root->getAttribute('stopOnError'), FALSE + ); + } + + if ($root->hasAttribute('stopOnFailure')) { + $result['stopOnFailure'] = $this->getBoolean( + (string)$root->getAttribute('stopOnFailure'), FALSE + ); + } + + if ($root->hasAttribute('stopOnIncomplete')) { + $result['stopOnIncomplete'] = $this->getBoolean( + (string)$root->getAttribute('stopOnIncomplete'), FALSE + ); + } + + if ($root->hasAttribute('stopOnSkipped')) { + $result['stopOnSkipped'] = $this->getBoolean( + (string)$root->getAttribute('stopOnSkipped'), FALSE + ); + } + + if ($root->hasAttribute('testSuiteLoaderClass')) { + $result['testSuiteLoaderClass'] = (string)$root->getAttribute( + 'testSuiteLoaderClass' + ); + } + + if ($root->hasAttribute('testSuiteLoaderFile')) { + $result['testSuiteLoaderFile'] = (string)$root->getAttribute( + 'testSuiteLoaderFile' + ); + } + + if ($root->hasAttribute('printerClass')) { + $result['printerClass'] = (string)$root->getAttribute( + 'printerClass' + ); + } + + if ($root->hasAttribute('printerFile')) { + $result['printerFile'] = (string)$root->getAttribute( + 'printerFile' + ); + } + + if ($root->hasAttribute('timeoutForSmallTests')) { + $result['timeoutForSmallTests'] = $this->getInteger( + (string)$root->getAttribute('timeoutForSmallTests'), 1 + ); + } + + if ($root->hasAttribute('timeoutForMediumTests')) { + $result['timeoutForMediumTests'] = $this->getInteger( + (string)$root->getAttribute('timeoutForMediumTests'), 10 + ); + } + + if ($root->hasAttribute('timeoutForLargeTests')) { + $result['timeoutForLargeTests'] = $this->getInteger( + (string)$root->getAttribute('timeoutForLargeTests'), 60 + ); + } + + if ($root->hasAttribute('strict')) { + $result['strict'] = $this->getBoolean( + (string)$root->getAttribute('strict'), FALSE + ); + } + + if ($root->hasAttribute('verbose')) { + $result['verbose'] = $this->getBoolean( + (string)$root->getAttribute('verbose'), FALSE + ); + } + + return $result; + } + + /** + * Returns the SeleniumTestCase browser configuration. + * + * @return array + * @since Method available since Release 3.2.9 + */ + public function getSeleniumBrowserConfiguration() + { + $result = array(); + + foreach ($this->xpath->query('selenium/browser') as $config) { + $name = (string)$config->getAttribute('name'); + $browser = (string)$config->getAttribute('browser'); + + if ($config->hasAttribute('host')) { + $host = (string)$config->getAttribute('host'); + } else { + $host = 'localhost'; + } + + if ($config->hasAttribute('port')) { + $port = $this->getInteger( + (string)$config->getAttribute('port'), 4444 + ); + } else { + $port = 4444; + } + + if ($config->hasAttribute('timeout')) { + $timeout = $this->getInteger( + (string)$config->getAttribute('timeout'), 30000 + ); + } else { + $timeout = 30000; + } + + $result[] = array( + 'name' => $name, + 'browser' => $browser, + 'host' => $host, + 'port' => $port, + 'timeout' => $timeout + ); + } + + return $result; + } + + /** + * Returns the test suite configuration. + * + * @return PHPUnit_Framework_TestSuite + * @since Method available since Release 3.2.1 + */ + public function getTestSuiteConfiguration($testSuiteFilter=null) + { + $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); + + if ($testSuiteNodes->length == 0) { + $testSuiteNodes = $this->xpath->query('testsuite'); + } + + if ($testSuiteNodes->length == 1) { + return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); + } + + if ($testSuiteNodes->length > 1) { + $suite = new PHPUnit_Framework_TestSuite; + + foreach ($testSuiteNodes as $testSuiteNode) { + $suite->addTestSuite( + $this->getTestSuite($testSuiteNode, $testSuiteFilter) + ); + } + + return $suite; + } + } + + /** + * @param DOMElement $testSuiteNode + * @return PHPUnit_Framework_TestSuite + * @since Method available since Release 3.4.0 + */ + protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter=null) + { + if ($testSuiteNode->hasAttribute('name')) { + $suite = new PHPUnit_Framework_TestSuite( + (string)$testSuiteNode->getAttribute('name') + ); + } else { + $suite = new PHPUnit_Framework_TestSuite; + } + + $exclude = array(); + + foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) { + $exclude[] = (string)$excludeNode->nodeValue; + } + + $fileIteratorFacade = new File_Iterator_Facade; + + foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) { + if ($testSuiteFilter && $directoryNode->parentNode->getAttribute('name') != $testSuiteFilter) { + continue; + } + + $directory = (string)$directoryNode->nodeValue; + + if (empty($directory)) { + continue; + } + + if ($directoryNode->hasAttribute('phpVersion')) { + $phpVersion = (string)$directoryNode->getAttribute('phpVersion'); + } else { + $phpVersion = PHP_VERSION; + } + + if ($directoryNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = (string)$directoryNode->getAttribute('phpVersionOperator'); + } else { + $phpVersionOperator = '>='; + } + + if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { + continue; + } + + if ($directoryNode->hasAttribute('prefix')) { + $prefix = (string)$directoryNode->getAttribute('prefix'); + } else { + $prefix = ''; + } + + if ($directoryNode->hasAttribute('suffix')) { + $suffix = (string)$directoryNode->getAttribute('suffix'); + } else { + $suffix = 'Test.php'; + } + + $files = $fileIteratorFacade->getFilesAsArray( + $this->toAbsolutePath($directory), + $suffix, + $prefix, + $exclude + ); + $suite->addTestFiles($files); + } + + foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) { + if ($testSuiteFilter && $fileNode->parentNode->getAttribute('name') != $testSuiteFilter) { + continue; + } + + $file = (string)$fileNode->nodeValue; + + if (empty($file)) { + continue; + } + + // Get the absolute path to the file + $file = $fileIteratorFacade->getFilesAsArray($file); + + if (!isset($file[0])) { + continue; + } + + $file = $file[0]; + + if ($fileNode->hasAttribute('phpVersion')) { + $phpVersion = (string)$fileNode->getAttribute('phpVersion'); + } else { + $phpVersion = PHP_VERSION; + } + + if ($fileNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = (string)$fileNode->getAttribute('phpVersionOperator'); + } else { + $phpVersionOperator = '>='; + } + + if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { + continue; + } + + $suite->addTestFile($file); + } + + return $suite; + } + + /** + * @param string $value + * @param boolean $default + * @return boolean + * @since Method available since Release 3.2.3 + */ + protected function getBoolean($value, $default) + { + if (strtolower($value) == 'false') { + return FALSE; + } + + else if (strtolower($value) == 'true') { + return TRUE; + } + + return $default; + } + + /** + * @param string $value + * @param boolean $default + * @return boolean + * @since Method available since Release 3.6.0 + */ + protected function getInteger($value, $default) + { + if (is_numeric($value)) { + return (int)$value; + } + + return $default; + } + + /** + * @param string $query + * @return array + * @since Method available since Release 3.2.3 + */ + protected function readFilterDirectories($query) + { + $directories = array(); + + foreach ($this->xpath->query($query) as $directory) { + if ($directory->hasAttribute('prefix')) { + $prefix = (string)$directory->getAttribute('prefix'); + } else { + $prefix = ''; + } + + if ($directory->hasAttribute('suffix')) { + $suffix = (string)$directory->getAttribute('suffix'); + } else { + $suffix = '.php'; + } + + if ($directory->hasAttribute('group')) { + $group = (string)$directory->getAttribute('group'); + } else { + $group = 'DEFAULT'; + } + + $directories[] = array( + 'path' => $this->toAbsolutePath((string)$directory->nodeValue), + 'prefix' => $prefix, + 'suffix' => $suffix, + 'group' => $group + ); + } + + return $directories; + } + + /** + * @param string $query + * @return array + * @since Method available since Release 3.2.3 + */ + protected function readFilterFiles($query) + { + $files = array(); + + foreach ($this->xpath->query($query) as $file) { + $files[] = $this->toAbsolutePath((string)$file->nodeValue); + } + + return $files; + } + + /** + * @param string $path + * @param boolean $useIncludePath + * @return string + * @since Method available since Release 3.5.0 + */ + protected function toAbsolutePath($path, $useIncludePath = FALSE) + { + // Check whether the path is already absolute. + if ($path[0] === '/' || $path[0] === '\\' || + (strlen($path) > 3 && ctype_alpha($path[0]) && + $path[1] === ':' && ($path[2] === '\\' || $path[2] === '/'))) { + return $path; + } + + // Check whether a stream is used. + if (strpos($path, '://') !== FALSE) { + return $path; + } + + $file = dirname($this->filename) . DIRECTORY_SEPARATOR . $path; + + if ($useIncludePath && !file_exists($file)) { + $includePathFile = stream_resolve_include_path($path); + + if ($includePathFile) { + $file = $includePathFile; + } + } + + return $file; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature.php b/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature.php new file mode 100644 index 0000000..ee3f032 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature.php @@ -0,0 +1,102 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Ralph Schindler + * @author Sebastian Bergmann + * @copyright 2002-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.7 + */ + +/** + * Class to hold the information about a deprecated feature that was used + * + * @package PHPUnit + * @subpackage Framework + * @author Ralph Schindler + * @author Sebastian Bergmann + * @copyright 2002-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Interface available since Release 3.5.7 + */ +class PHPUnit_Util_DeprecatedFeature +{ + /** + * @var array + */ + protected $traceInfo = array(); + + /** + * @var string + */ + protected $message = NULL; + + /** + * @param string $message + * @param array $traceInfo + */ + public function __construct($message, array $traceInfo = array()) + { + $this->message = $message; + $this->traceInfo = $traceInfo; + } + + /** + * Build a string representation of the deprecated feature that was raised + * + * @return string + */ + public function __toString() + { + $string = ''; + + if (isset($this->traceInfo['file'])) { + $string .= $this->traceInfo['file']; + + if (isset($this->traceInfo['line'])) { + $string .= ':' . $this->traceInfo['line'] . ' - '; + } + } + + $string .= $this->message; + + return $string; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php b/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php new file mode 100644 index 0000000..721bdff --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php @@ -0,0 +1,201 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Ralph Schindler + * @author Sebastian Bergmann + * @copyright 2002-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.7 + */ + +/** + * Test Listener that tracks the usage of deprecated features. + * + * @package PHPUnit + * @subpackage Framework + * @author Ralph Schindler + * @author Sebastian Bergmann + * @copyright 2002-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.7 + */ +class PHPUnit_Util_DeprecatedFeature_Logger implements PHPUnit_Framework_TestListener +{ + /** + * @var PHPUnit_Framework_TestCase + */ + protected static $currentTest = NULL; + + /** + * This is the publically accessible API for notifying the system that a + * deprecated feature has been used. + * + * If it is run via a TestRunner and the test extends + * PHPUnit_Framework_TestCase, then this will inject the result into the + * test runner for display, if not, it will throw the notice to STDERR. + * + * @param string $message + * @param int|bool $backtraceDepth + */ + public static function log($message, $backtraceDepth = 2) + { + if ($backtraceDepth !== FALSE) { + $trace = debug_backtrace(FALSE); + + if (is_int($backtraceDepth)) { + $traceItem = $trace[$backtraceDepth]; + } + + if (!isset($traceItem['file'])) { + $reflectionClass = new ReflectionClass($traceItem['class']); + $traceItem['file'] = $reflectionClass->getFileName(); + } + + if (!isset($traceItem['line']) && + isset($traceItem['class']) && + isset($traceItem['function'])) { + if (!isset($reflectionClass)) { + $reflectionClass = new ReflectionClass($traceItem['class']); + } + + $method = $reflectionClass->getMethod($traceItem['function']); + $traceItem['line'] = '(between ' . $method->getStartLine() . + ' and ' . $method->getEndLine() . ')'; + } + } + + $deprecatedFeature = new PHPUnit_Util_DeprecatedFeature( + $message, $traceItem + ); + + if (self::$currentTest instanceof PHPUnit_Framework_TestCase) { + $result = self::$currentTest->getTestResultObject(); + $result->addDeprecatedFeature($deprecatedFeature); + } else { + file_put_contents('php://stderr', $deprecatedFeature); + } + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + self::$currentTest = $test; + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + self::$currentTest = NULL; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Diff.php b/vendor/phpunit/phpunit/PHPUnit/Util/Diff.php new file mode 100644 index 0000000..b8b2f37 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Diff.php @@ -0,0 +1,292 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Diff implementation. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Util_Diff +{ + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @return string + */ + public static function diff($from, $to) + { + $buffer= "--- Expected\n+++ Actual\n"; + $diff = self::diffToArray($from,$to); + + $inOld = FALSE; + $i = 0; + $old = array(); + + foreach ($diff as $line) { + if ($line[1] === 0 /* OLD */) { + if ($inOld === FALSE) { + $inOld = $i; + } + } + + else if ($inOld !== FALSE) { + if (($i - $inOld) > 5) { + $old[$inOld] = $i - 1; + } + + $inOld = FALSE; + } + + ++$i; + } + + $start = isset($old[0]) ? $old[0] : 0; + $end = count($diff); + $i = 0; + + if ($tmp = array_search($end, $old)) { + $end = $tmp; + } + + $newChunk = TRUE; + + for ($i = $start; $i < $end; $i++) { + if (isset($old[$i])) { + $buffer .= "\n"; + $newChunk = TRUE; + $i = $old[$i]; + } + + if ($newChunk) { + $buffer .= "@@ @@\n"; + $newChunk = FALSE; + } + + if ($diff[$i][1] === 1 /* ADDED */) { + $buffer .= '+' . $diff[$i][0] . "\n"; + } + + else if ($diff[$i][1] === 2 /* REMOVED */) { + $buffer .= '-' . $diff[$i][0] . "\n"; + } + + else { + $buffer .= ' ' . $diff[$i][0] . "\n"; + } + } + + return $buffer; + } + + /** + * Returns the diff between two arrays or strings as array. + * + * every array-entry containts two elements: + * - [0] => string $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @return array + */ + public static function diffToArray($from, $to) + { + preg_match_all('(\r\n|\r|\n)', $from, $fromMatches); + preg_match_all('(\r\n|\r|\n)', $to, $toMatches); + + if (is_string($from)) { + $from = preg_split('(\r\n|\r|\n)', $from); + } + + if (is_string($to)) { + $to = preg_split('(\r\n|\r|\n)', $to); + } + + $start = array(); + $end = array(); + $fromLength = count($from); + $toLength = count($to); + $length = min($fromLength, $toLength); + + for ($i = 0; $i < $length; ++$i) { + if ($from[$i] === $to[$i]) { + $start[] = $from[$i]; + unset($from[$i], $to[$i]); + } else { + break; + } + } + + $length -= $i; + + for ($i = 1; $i < $length; ++$i) { + if ($from[$fromLength - $i] === $to[$toLength - $i]) { + array_unshift($end, $from[$fromLength - $i]); + unset($from[$fromLength - $i], $to[$toLength - $i]); + } else { + break; + } + } + + $common = self::longestCommonSubsequence( + array_values($from), array_values($to) + ); + + $diff = array(); + $line = 0; + + if (isset($fromMatches[0]) && $toMatches[0] && + count($fromMatches[0]) === count($toMatches[0]) && + $fromMatches[0] !== $toMatches[0]) { + $diff[] = array( + '#Warning: Strings contain different line endings!', 0 + ); + } + + foreach ($start as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + reset($from); + reset($to); + + foreach ($common as $token) { + while ((($fromToken = reset($from)) !== $token)) { + $diff[] = array(array_shift($from), 2 /* REMOVED */); + } + + while ((($toToken = reset($to)) !== $token)) { + $diff[] = array(array_shift($to), 1 /* ADDED */); + } + + $diff[] = array($token, 0 /* OLD */); + + array_shift($from); + array_shift($to); + } + + while (($token = array_shift($from)) !== NULL) { + $diff[] = array($token, 2 /* REMOVED */); + } + + while (($token = array_shift($to)) !== NULL) { + $diff[] = array($token, 1 /* ADDED */); + } + + foreach ($end as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + return $diff; + } + + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * @return array + */ + protected static function longestCommonSubsequence(array $from, array $to) + { + $common = array(); + $matrix = array(); + $fromLength = count($from); + $toLength = count($to); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i][0] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[0][$j] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $matrix[$i][$j] = max( + $matrix[$i-1][$j], + $matrix[$i][$j-1], + $from[$i-1] === $to[$j-1] ? $matrix[$i-1][$j-1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i-1] === $to[$j-1]) { + array_unshift($common, $from[$i-1]); + --$i; + --$j; + } + + else if ($matrix[$i][$j-1] > $matrix[$i-1][$j]) { + --$j; + } + + else { + --$i; + } + } + + return $common; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/ErrorHandler.php b/vendor/phpunit/phpunit/PHPUnit/Util/ErrorHandler.php new file mode 100644 index 0000000..1299b36 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/ErrorHandler.php @@ -0,0 +1,132 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +// Workaround for http://bugs.php.net/bug.php?id=47987, +// see https://github.com/sebastianbergmann/phpunit/issues#issue/125 for details +require_once 'PHPUnit/Framework/Error.php'; +require_once 'PHPUnit/Framework/Error/Notice.php'; +require_once 'PHPUnit/Framework/Error/Warning.php'; +require_once 'PHPUnit/Framework/Error/Deprecated.php'; + +/** + * Error handler that converts PHP errors and warnings to exceptions. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Util_ErrorHandler +{ + protected static $errorStack = array(); + + /** + * Returns the error stack. + * + * @return array + */ + public static function getErrorStack() + { + return self::$errorStack; + } + + /** + * @param integer $errno + * @param string $errstr + * @param string $errfile + * @param integer $errline + * @throws PHPUnit_Framework_Error + */ + public static function handleError($errno, $errstr, $errfile, $errline) + { + if (!($errno & error_reporting())) { + return FALSE; + } + + self::$errorStack[] = array($errno, $errstr, $errfile, $errline); + + $trace = debug_backtrace(FALSE); + array_shift($trace); + + foreach ($trace as $frame) { + if ($frame['function'] == '__toString') { + return FALSE; + } + } + + if ($errno == E_NOTICE || $errno == E_USER_NOTICE || $errno == E_STRICT) { + if (PHPUnit_Framework_Error_Notice::$enabled !== TRUE) { + return FALSE; + } + + $exception = 'PHPUnit_Framework_Error_Notice'; + } + + else if ($errno == E_WARNING || $errno == E_USER_WARNING) { + if (PHPUnit_Framework_Error_Warning::$enabled !== TRUE) { + return FALSE; + } + + $exception = 'PHPUnit_Framework_Error_Warning'; + } + + else if ($errno == E_DEPRECATED || $errno == E_USER_DEPRECATED) { + if (PHPUnit_Framework_Error_Deprecated::$enabled !== TRUE) { + return FALSE; + } + + $exception = 'PHPUnit_Framework_Error_Deprecated'; + } + + else { + $exception = 'PHPUnit_Framework_Error'; + } + + throw new $exception($errstr, $errno, $errfile, $errline); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Fileloader.php b/vendor/phpunit/phpunit/PHPUnit/Util/Fileloader.php new file mode 100644 index 0000000..7eeb7df --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Fileloader.php @@ -0,0 +1,107 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * Utility methods to load PHP sourcefiles. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.3.0 + */ +class PHPUnit_Util_Fileloader +{ + /** + * Checks if a PHP sourcefile is readable. + * The sourcefile is loaded through the load() method. + * + * @param string $filename + * @throws PHPUnit_Framework_Exception + */ + public static function checkAndLoad($filename) + { + $includePathFilename = stream_resolve_include_path($filename); + + if (!$includePathFilename || !is_readable($includePathFilename)) { + throw new PHPUnit_Framework_Exception( + sprintf('Cannot open file "%s".' . "\n", $filename) + ); + } + + self::load($includePathFilename); + + return $includePathFilename; + } + + /** + * Loads a PHP sourcefile. + * + * @param string $filename + * @return mixed + * @since Method available since Release 3.0.0 + */ + public static function load($filename) + { + $oldVariableNames = array_keys(get_defined_vars()); + + include_once $filename; + + $newVariables = get_defined_vars(); + $newVariableNames = array_diff( + array_keys($newVariables), $oldVariableNames + ); + + foreach ($newVariableNames as $variableName) { + if ($variableName != 'oldVariableNames') { + $GLOBALS[$variableName] = $newVariables[$variableName]; + } + } + + return $filename; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Filesystem.php b/vendor/phpunit/phpunit/PHPUnit/Util/Filesystem.php new file mode 100644 index 0000000..80cb119 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Filesystem.php @@ -0,0 +1,81 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Filesystem helpers. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Filesystem +{ + /** + * @var array + */ + protected static $buffer = array(); + + /** + * Maps class names to source file names: + * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php + * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php + * + * @param string $className + * @return string + * @since Method available since Release 3.4.0 + */ + public static function classNameToFilename($className) + { + return str_replace( + array('_', '\\'), + DIRECTORY_SEPARATOR, + $className + ) . '.php'; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Filter.php b/vendor/phpunit/phpunit/PHPUnit/Util/Filter.php new file mode 100644 index 0000000..3d8e4f5 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Filter.php @@ -0,0 +1,146 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Utility class for code filtering. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Util_Filter +{ + /** + * Filters stack frames from PHPUnit classes. + * + * @param Exception $e + * @param boolean $asString + * @return string + */ + public static function getFilteredStacktrace(Exception $e, $asString = TRUE) + { + $prefix = FALSE; + $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); + + if (substr($script, -5) == '.phar') { + $prefix = 'phar://' . $script . '/'; + } + + if (!defined('PHPUNIT_TESTSUITE')) { + $blacklist = PHPUnit_Util_GlobalState::phpunitFiles(); + } else { + $blacklist = array(); + } + + if ($asString === TRUE) { + $filteredStacktrace = ''; + } else { + $filteredStacktrace = array(); + } + + if ($e instanceof PHPUnit_Framework_SyntheticError) { + $eTrace = $e->getSyntheticTrace(); + $eFile = $e->getSyntheticFile(); + $eLine = $e->getSyntheticLine(); + } else { + if ($e->getPrevious()) { + $eTrace = $e->getPrevious()->getTrace(); + } else { + $eTrace = $e->getTrace(); + } + $eFile = $e->getFile(); + $eLine = $e->getLine(); + } + + if (!self::frameExists($eTrace, $eFile, $eLine)) { + array_unshift( + $eTrace, array('file' => $eFile, 'line' => $eLine) + ); + } + + foreach ($eTrace as $frame) { + if (isset($frame['file']) && is_file($frame['file']) && + !isset($blacklist[$frame['file']]) && + strpos($frame['file'], $prefix) !== 0 && + $frame['file'] !== $script) { + if ($asString === TRUE) { + $filteredStacktrace .= sprintf( + "%s:%s\n", + + $frame['file'], + isset($frame['line']) ? $frame['line'] : '?' + ); + } else { + $filteredStacktrace[] = $frame; + } + } + } + + return $filteredStacktrace; + } + + /** + * @param array $trace + * @param string $file + * @param int $line + * @return boolean + * @since Method available since Release 3.3.2 + */ + public static function frameExists(array $trace, $file, $line) + { + foreach ($trace as $frame) { + if (isset($frame['file']) && $frame['file'] == $file && + isset($frame['line']) && $frame['line'] == $line) { + return TRUE; + } + } + + return FALSE; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Getopt.php b/vendor/phpunit/phpunit/PHPUnit/Util/Getopt.php new file mode 100644 index 0000000..263558a --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Getopt.php @@ -0,0 +1,207 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Command-line options parsing class. + * + * @package PHPUnit + * @subpackage Util + * @author Andrei Zmievski + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Getopt +{ + public static function getopt(array $args, $short_options, $long_options = NULL) + { + if (empty($args)) { + return array(array(), array()); + } + + $opts = array(); + $non_opts = array(); + + if ($long_options) { + sort($long_options); + } + + if (isset($args[0][0]) && $args[0][0] != '-') { + array_shift($args); + } + + reset($args); + array_map('trim', $args); + + while (list($i, $arg) = each($args)) { + if ($arg == '') { + continue; + } + + if ($arg == '--') { + $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); + break; + } + + if ($arg[0] != '-' || + (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) { + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; + } + + elseif (strlen($arg) > 1 && $arg[1] == '-') { + self::parseLongOption( + substr($arg, 2), $long_options, $opts, $args + ); + } + + else { + self::parseShortOption( + substr($arg, 1), $short_options, $opts, $args + ); + } + } + + return array($opts, $non_opts); + } + + protected static function parseShortOption($arg, $short_options, &$opts, &$args) + { + $argLen = strlen($arg); + + for ($i = 0; $i < $argLen; $i++) { + $opt = $arg[$i]; + $opt_arg = NULL; + + if (($spec = strstr($short_options, $opt)) === FALSE || + $arg[$i] == ':') { + throw new PHPUnit_Framework_Exception( + "unrecognized option -- $opt" + ); + } + + if (strlen($spec) > 1 && $spec[1] == ':') { + if (strlen($spec) > 2 && $spec[2] == ':') { + if ($i + 1 < $argLen) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + } else { + if ($i + 1 < $argLen) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + + else if (list(, $opt_arg) = each($args)) { + } + + else { + throw new PHPUnit_Framework_Exception( + "option requires an argument -- $opt" + ); + } + } + } + + $opts[] = array($opt, $opt_arg); + } + } + + protected static function parseLongOption($arg, $long_options, &$opts, &$args) + { + $count = count($long_options); + $list = explode('=', $arg); + $opt = $list[0]; + $opt_arg = NULL; + + if (count($list) > 1) { + $opt_arg = $list[1]; + } + + $opt_len = strlen($opt); + + for ($i = 0; $i < $count; $i++) { + $long_opt = $long_options[$i]; + $opt_start = substr($long_opt, 0, $opt_len); + + if ($opt_start != $opt) { + continue; + } + + $opt_rest = substr($long_opt, $opt_len); + + if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count && + $opt == substr($long_options[$i+1], 0, $opt_len)) { + throw new PHPUnit_Framework_Exception( + "option --$opt is ambiguous" + ); + } + + if (substr($long_opt, -1) == '=') { + if (substr($long_opt, -2) != '==') { + if (!strlen($opt_arg) && + !(list(, $opt_arg) = each($args))) { + throw new PHPUnit_Framework_Exception( + "option --$opt requires an argument" + ); + } + } + } + + else if ($opt_arg) { + throw new PHPUnit_Framework_Exception( + "option --$opt doesn't allow an argument" + ); + } + + $opts[] = array('--' . $opt, $opt_arg); + return; + } + + throw new PHPUnit_Framework_Exception("unrecognized option --$opt"); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php b/vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php new file mode 100644 index 0000000..425f0d4 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php @@ -0,0 +1,427 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Util_GlobalState +{ + /** + * @var array + */ + protected static $globals = array(); + + /** + * @var array + */ + protected static $staticAttributes = array(); + + /** + * @var array + */ + protected static $superGlobalArrays = array( + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST' + ); + + /** + * @var array + */ + protected static $superGlobalArraysLong = array( + 'HTTP_ENV_VARS', + 'HTTP_POST_VARS', + 'HTTP_GET_VARS', + 'HTTP_COOKIE_VARS', + 'HTTP_SERVER_VARS', + 'HTTP_POST_FILES' + ); + + /** + * @var array + */ + protected static $phpunitFiles; + + public static function backupGlobals(array $blacklist) + { + self::$globals = array(); + $superGlobalArrays = self::getSuperGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + if (!in_array($superGlobalArray, $blacklist)) { + self::backupSuperGlobalArray($superGlobalArray); + } + } + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + !in_array($key, $blacklist) && + !$GLOBALS[$key] instanceof Closure) { + self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]); + } + } + } + + public static function restoreGlobals(array $blacklist) + { + if (ini_get('register_long_arrays') == '1') { + $superGlobalArrays = array_merge( + self::$superGlobalArrays, self::$superGlobalArraysLong + ); + } else { + $superGlobalArrays = self::$superGlobalArrays; + } + + foreach ($superGlobalArrays as $superGlobalArray) { + if (!in_array($superGlobalArray, $blacklist)) { + self::restoreSuperGlobalArray($superGlobalArray); + } + } + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + !in_array($key, $blacklist)) { + if (isset(self::$globals['GLOBALS'][$key])) { + $GLOBALS[$key] = unserialize( + self::$globals['GLOBALS'][$key] + ); + } else { + unset($GLOBALS[$key]); + } + } + } + + self::$globals = array(); + } + + protected static function backupSuperGlobalArray($superGlobalArray) + { + self::$globals[$superGlobalArray] = array(); + + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray])) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + self::$globals[$superGlobalArray][$key] = serialize($value); + } + } + } + + protected static function restoreSuperGlobalArray($superGlobalArray) + { + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray]) && + isset(self::$globals[$superGlobalArray])) { + $keys = array_keys( + array_merge( + $GLOBALS[$superGlobalArray], self::$globals[$superGlobalArray] + ) + ); + + foreach ($keys as $key) { + if (isset(self::$globals[$superGlobalArray][$key])) { + $GLOBALS[$superGlobalArray][$key] = unserialize( + self::$globals[$superGlobalArray][$key] + ); + } else { + unset($GLOBALS[$superGlobalArray][$key]); + } + } + } + + self::$globals[$superGlobalArray] = array(); + } + + public static function getIncludedFilesAsString() + { + $blacklist = self::phpunitFiles(); + $files = get_included_files(); + $prefix = FALSE; + $result = ''; + $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); + + if (substr($script, -5) == '.phar') { + $prefix = 'phar://' . $script . '/'; + } + + for ($i = count($files) - 1; $i > 0; $i--) { + $file = $files[$i]; + + if ($prefix !== FALSE) { + $file = str_replace($prefix, '', $file); + } + + if (!isset($blacklist[$file]) && is_file($file)) { + $result = 'require_once \'' . $file . "';\n" . $result; + } + } + + return $result; + } + + public static function getConstantsAsString() + { + $constants = get_defined_constants(TRUE); + $result = ''; + + if (isset($constants['user'])) { + foreach ($constants['user'] as $name => $value) { + $result .= sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + self::exportVariable($value) + ); + } + } + + return $result; + } + + public static function getGlobalsAsString() + { + $result = ''; + $superGlobalArrays = self::getSuperGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray])) { + foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) { + if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { + continue; + } + + $result .= sprintf( + '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", + $superGlobalArray, + $key, + self::exportVariable($GLOBALS[$superGlobalArray][$key]) + ); + } + } + } + + $blacklist = $superGlobalArrays; + $blacklist[] = 'GLOBALS'; + $blacklist[] = '_PEAR_Config_instance'; + + foreach (array_keys($GLOBALS) as $key) { + if (!in_array($key, $blacklist) && !$GLOBALS[$key] instanceof Closure) { + $result .= sprintf( + '$GLOBALS[\'%s\'] = %s;' . "\n", + $key, + self::exportVariable($GLOBALS[$key]) + ); + } + } + + return $result; + } + + protected static function getSuperGlobalArrays() + { + if (ini_get('register_long_arrays') == '1') { + return array_merge( + self::$superGlobalArrays, self::$superGlobalArraysLong + ); + } else { + return self::$superGlobalArrays; + } + } + + public static function backupStaticAttributes(array $blacklist) + { + self::$staticAttributes = array(); + $declaredClasses = get_declared_classes(); + $declaredClassesNum = count($declaredClasses); + + for ($i = $declaredClassesNum - 1; $i >= 0; $i--) { + if (strpos($declaredClasses[$i], 'PHPUnit') !== 0 && + strpos($declaredClasses[$i], 'File_Iterator') !== 0 && + strpos($declaredClasses[$i], 'PHP_CodeCoverage') !== 0 && + strpos($declaredClasses[$i], 'PHP_Invoker') !== 0 && + strpos($declaredClasses[$i], 'PHP_Timer') !== 0 && + strpos($declaredClasses[$i], 'PHP_TokenStream') !== 0 && + strpos($declaredClasses[$i], 'Symfony') !== 0 && + strpos($declaredClasses[$i], 'Text_Template') !== 0 && + !$declaredClasses[$i] instanceof PHPUnit_Framework_Test) { + $class = new ReflectionClass($declaredClasses[$i]); + + if (!$class->isUserDefined()) { + break; + } + + $backup = array(); + + foreach ($class->getProperties() as $attribute) { + if ($attribute->isStatic()) { + $name = $attribute->getName(); + + if (!isset($blacklist[$declaredClasses[$i]]) || + !in_array($name, $blacklist[$declaredClasses[$i]])) { + $attribute->setAccessible(TRUE); + $value = $attribute->getValue(); + + if (!$value instanceof Closure) { + $backup[$name] = serialize($value); + } + } + } + } + + if (!empty($backup)) { + self::$staticAttributes[$declaredClasses[$i]] = $backup; + } + } + } + } + + public static function restoreStaticAttributes() + { + foreach (self::$staticAttributes as $className => $staticAttributes) { + foreach ($staticAttributes as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setAccessible(TRUE); + $reflector->setValue(unserialize($value)); + } + } + + self::$staticAttributes = array(); + } + + protected static function exportVariable($variable) + { + if (is_scalar($variable) || is_null($variable) || + (is_array($variable) && self::arrayOnlyContainsScalars($variable))) { + return var_export($variable, TRUE); + } + + return 'unserialize(\'' . + str_replace("'", "\'", serialize($variable)) . + '\')'; + } + + protected static function arrayOnlyContainsScalars(array $array) + { + $result = TRUE; + + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } + + else if (!is_scalar($element) && !is_null($element)) { + $result = FALSE; + } + + if ($result === FALSE) { + break; + } + } + + return $result; + } + + /** + * @return array + * @since Method available since Release 3.6.0 + */ + public static function phpunitFiles() + { + if (self::$phpunitFiles === NULL) { + self::addDirectoryContainingClassToPHPUnitFilesList('File_Iterator'); + self::addDirectoryContainingClassToPHPUnitFilesList('PHP_CodeCoverage'); + self::addDirectoryContainingClassToPHPUnitFilesList('PHP_Invoker'); + self::addDirectoryContainingClassToPHPUnitFilesList('PHP_Timer'); + self::addDirectoryContainingClassToPHPUnitFilesList('PHP_Token'); + self::addDirectoryContainingClassToPHPUnitFilesList('PHPUnit_Framework_TestCase', 2); + self::addDirectoryContainingClassToPHPUnitFilesList('PHPUnit_Extensions_Database_TestCase', 2); + self::addDirectoryContainingClassToPHPUnitFilesList('PHPUnit_Framework_MockObject_Generator', 2); + self::addDirectoryContainingClassToPHPUnitFilesList('PHPUnit_Extensions_SeleniumTestCase', 2); + self::addDirectoryContainingClassToPHPUnitFilesList('PHPUnit_Extensions_Story_TestCase', 2); + self::addDirectoryContainingClassToPHPUnitFilesList('Text_Template'); + } + + return self::$phpunitFiles; + } + + /** + * @param string $className + * @param integer $parent + * @since Method available since Release 3.7.2 + */ + protected static function addDirectoryContainingClassToPHPUnitFilesList($className, $parent = 1) + { + if (!class_exists($className)) { + return; + } + + $reflector = new ReflectionClass($className); + $directory = $reflector->getFileName(); + + for ($i = 0; $i < $parent; $i++) { + $directory = dirname($directory); + } + + $facade = new File_Iterator_Facade; + + foreach ($facade->getFilesAsArray($directory, '.php') as $file) { + self::$phpunitFiles[$file] = TRUE; + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/InvalidArgumentHelper.php b/vendor/phpunit/phpunit/PHPUnit/Util/InvalidArgumentHelper.php new file mode 100644 index 0000000..b5212e8 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/InvalidArgumentHelper.php @@ -0,0 +1,80 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Factory for PHPUnit_Framework_Exception objects that are used to describe + * invalid arguments passed to a function or method. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Util_InvalidArgumentHelper +{ + /** + * @param integer $argument + * @param string $type + * @param mixed $value + */ + public static function factory($argument, $type, $value = NULL) + { + $stack = debug_backtrace(FALSE); + + return new PHPUnit_Framework_Exception( + sprintf( + 'Argument #%d%sof %s::%s() must be a %s', + $argument, + $value !== NULL ? ' (' . $value . ')' : ' ', + $stack[1]['class'], + $stack[1]['function'], + $type + ) + ); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Log/JSON.php b/vendor/phpunit/phpunit/PHPUnit/Util/Log/JSON.php new file mode 100644 index 0000000..aa56e56 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Log/JSON.php @@ -0,0 +1,254 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_Log + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * A TestListener that generates JSON messages. + * + * @package PHPUnit + * @subpackage Util_Log + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Log_JSON extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var string + */ + protected $currentTestSuiteName = ''; + + /** + * @var string + */ + protected $currentTestName = ''; + + /** + * @var boolean + * @access private + */ + protected $currentTestPass = TRUE; + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + $e->getMessage(), + $test + ); + + $this->currentTestPass = FALSE; + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->writeCase( + 'fail', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + $e->getMessage(), + $test + ); + + $this->currentTestPass = FALSE; + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + 'Incomplete Test: ' . $e->getMessage(), + $test + ); + + $this->currentTestPass = FALSE; + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + 'Skipped Test: ' . $e->getMessage(), + $test + ); + + $this->currentTestPass = FALSE; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->currentTestSuiteName = $suite->getName(); + $this->currentTestName = ''; + + $this->write( + array( + 'event' => 'suiteStart', + 'suite' => $this->currentTestSuiteName, + 'tests' => count($suite) + ) + ); + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->currentTestSuiteName = ''; + $this->currentTestName = ''; + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $this->currentTestName = PHPUnit_Util_Test::describe($test); + $this->currentTestPass = TRUE; + + $this->write( + array( + 'event' => 'testStart', + 'suite' => $this->currentTestSuiteName, + 'test' => $this->currentTestName + ) + ); + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if ($this->currentTestPass) { + $this->writeCase('pass', $time, array(), '', $test); + } + } + + /** + * @param string $status + * @param float $time + * @param array $trace + * @param string $message + */ + protected function writeCase($status, $time, array $trace = array(), $message = '', $test = NULL) + { + $output = ''; + if ($test !== NULL && $test->hasOutput()) { + $output = $test->getActualOutput(); + } + $this->write( + array( + 'event' => 'test', + 'suite' => $this->currentTestSuiteName, + 'test' => $this->currentTestName, + 'status' => $status, + 'time' => $time, + 'trace' => $trace, + 'message' => PHPUnit_Util_String::convertToUtf8($message), + 'output' => $output, + ) + ); + } + + /** + * @param string $buffer + */ + public function write($buffer) + { + if (defined('JSON_PRETTY_PRINT')) { + parent::write(json_encode($buffer, JSON_PRETTY_PRINT)); + } else { + parent::write(json_encode($buffer)); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php b/vendor/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php new file mode 100644 index 0000000..1234101 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php @@ -0,0 +1,482 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_Log + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * A TestListener that generates a logfile of the test execution in XML markup. + * + * The XML markup used is the same as the one that is used by the JUnit Ant task. + * + * @package PHPUnit + * @subpackage Util_Log + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_Log_JUnit extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * @var DOMElement + */ + protected $root; + + /** + * @var boolean + */ + protected $logIncompleteSkipped = FALSE; + + /** + * @var boolean + */ + protected $writeDocument = TRUE; + + /** + * @var DOMElement[] + */ + protected $testSuites = array(); + + /** + * @var integer[] + */ + protected $testSuiteTests = array(0); + + /** + * @var integer[] + */ + protected $testSuiteAssertions = array(0); + + /** + * @var integer[] + */ + protected $testSuiteErrors = array(0); + + /** + * @var integer[] + */ + protected $testSuiteFailures = array(0); + + /** + * @var integer[] + */ + protected $testSuiteTimes = array(0); + + /** + * @var integer + */ + protected $testSuiteLevel = 0; + + /** + * @var DOMElement + */ + protected $currentTestCase = NULL; + + /** + * @var boolean + */ + protected $attachCurrentTestCase = TRUE; + + /** + * Constructor. + * + * @param mixed $out + * @param boolean $logIncompleteSkipped + */ + public function __construct($out = NULL, $logIncompleteSkipped = FALSE) + { + $this->document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = TRUE; + + $this->root = $this->document->createElement('testsuites'); + $this->document->appendChild($this->root); + + parent::__construct($out); + + $this->logIncompleteSkipped = $logIncompleteSkipped; + } + + /** + * Flush buffer and close output. + * + */ + public function flush() + { + if ($this->writeDocument === TRUE) { + $this->write($this->getXML()); + } + + parent::flush(); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->currentTestCase !== NULL) { + if ($test instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + + $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . + "\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e); + + $error = $this->document->createElement( + 'error', PHPUnit_Util_XML::prepareString($buffer) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if ($this->currentTestCase !== NULL) { + if (!$test instanceof PHPUnit_Framework_Warning) { + if ($test instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + + $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . + "\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e); + + $failure = $this->document->createElement( + 'failure', PHPUnit_Util_XML::prepareString($buffer) + ); + + $failure->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($failure); + + $this->testSuiteFailures[$this->testSuiteLevel]++; + } + } + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->logIncompleteSkipped && $this->currentTestCase !== NULL) { + $error = $this->document->createElement( + 'error', + PHPUnit_Util_XML::prepareString( + "Incomplete Test\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e) + ) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } else { + $this->attachCurrentTestCase = FALSE; + } + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->logIncompleteSkipped && $this->currentTestCase !== NULL) { + $error = $this->document->createElement( + 'error', + PHPUnit_Util_XML::prepareString( + "Skipped Test\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e) + ) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } else { + $this->attachCurrentTestCase = FALSE; + } + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $testSuite = $this->document->createElement('testsuite'); + $testSuite->setAttribute('name', $suite->getName()); + + if (class_exists($suite->getName(), FALSE)) { + try { + $class = new ReflectionClass($suite->getName()); + + $testSuite->setAttribute('file', $class->getFileName()); + + $packageInformation = PHPUnit_Util_Class::getPackageInformation( + $suite->getName(), $class->getDocComment() + ); + + if (!empty($packageInformation['namespace'])) { + $testSuite->setAttribute( + 'namespace', $packageInformation['namespace'] + ); + } + + if (!empty($packageInformation['fullPackage'])) { + $testSuite->setAttribute( + 'fullPackage', $packageInformation['fullPackage'] + ); + } + + if (!empty($packageInformation['category'])) { + $testSuite->setAttribute( + 'category', $packageInformation['category'] + ); + } + + if (!empty($packageInformation['package'])) { + $testSuite->setAttribute( + 'package', $packageInformation['package'] + ); + } + + if (!empty($packageInformation['subpackage'])) { + $testSuite->setAttribute( + 'subpackage', $packageInformation['subpackage'] + ); + } + } + + catch (ReflectionException $e) { + } + } + + if ($this->testSuiteLevel > 0) { + $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); + } else { + $this->root->appendChild($testSuite); + } + + $this->testSuiteLevel++; + $this->testSuites[$this->testSuiteLevel] = $testSuite; + $this->testSuiteTests[$this->testSuiteLevel] = 0; + $this->testSuiteAssertions[$this->testSuiteLevel] = 0; + $this->testSuiteErrors[$this->testSuiteLevel] = 0; + $this->testSuiteFailures[$this->testSuiteLevel] = 0; + $this->testSuiteTimes[$this->testSuiteLevel] = 0; + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'tests', $this->testSuiteTests[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'assertions', $this->testSuiteAssertions[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'failures', $this->testSuiteFailures[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'errors', $this->testSuiteErrors[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'time', sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]) + ); + + if ($this->testSuiteLevel > 1) { + $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; + $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; + $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; + $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; + $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; + } + + $this->testSuiteLevel--; + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if (!$test instanceof PHPUnit_Framework_Warning) { + $testCase = $this->document->createElement('testcase'); + $testCase->setAttribute('name', $test->getName()); + + if ($test instanceof PHPUnit_Framework_TestCase) { + $class = new ReflectionClass($test); + $methodName = $test->getName(); + + if ($class->hasMethod($methodName)) { + $method = $class->getMethod($test->getName()); + + $testCase->setAttribute('class', $class->getName()); + $testCase->setAttribute('file', $class->getFileName()); + $testCase->setAttribute('line', $method->getStartLine()); + } + } + + $this->currentTestCase = $testCase; + } + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if (!$test instanceof PHPUnit_Framework_Warning) { + if ($this->attachCurrentTestCase) { + if ($test instanceof PHPUnit_Framework_TestCase) { + $numAssertions = $test->getNumAssertions(); + $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; + + $this->currentTestCase->setAttribute( + 'assertions', $numAssertions + ); + } + + $this->currentTestCase->setAttribute( + 'time', sprintf('%F', $time) + ); + + $this->testSuites[$this->testSuiteLevel]->appendChild( + $this->currentTestCase + ); + + $this->testSuiteTests[$this->testSuiteLevel]++; + $this->testSuiteTimes[$this->testSuiteLevel] += $time; + } + } + + $this->attachCurrentTestCase = TRUE; + $this->currentTestCase = NULL; + } + + /** + * Returns the XML as a string. + * + * @return string + * @since Method available since Release 2.2.0 + */ + public function getXML() + { + return $this->document->saveXML(); + } + + /** + * Enables or disables the writing of the document + * in flush(). + * + * This is a "hack" needed for the integration of + * PHPUnit with Phing. + * + * @return string + * @since Method available since Release 2.2.0 + */ + public function setWriteDocument($flag) + { + if (is_bool($flag)) { + $this->writeDocument = $flag; + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Log/TAP.php b/vendor/phpunit/phpunit/PHPUnit/Util/Log/TAP.php new file mode 100644 index 0000000..bec5269 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Log/TAP.php @@ -0,0 +1,255 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_Log + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +if (!class_exists('Symfony\\Component\\Yaml\\Dumper') && + stream_resolve_include_path('Symfony/Component/Yaml/Dumper.php')) { + require_once 'Symfony/Component/Yaml/Dumper.php'; +} + +/** + * A TestListener that generates a logfile of the + * test execution using the Test Anything Protocol (TAP). + * + * @package PHPUnit + * @subpackage Util_Log + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var integer + */ + protected $testNumber = 0; + + /** + * @var integer + */ + protected $testSuiteLevel = 0; + + /** + * @var boolean + */ + protected $testSuccessful = TRUE; + + /** + * Constructor. + * + * @param mixed $out + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.3.4 + */ + public function __construct($out = NULL) + { + parent::__construct($out); + $this->write("TAP version 13\n"); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeNotOk($test, 'Error'); + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->writeNotOk($test, 'Failure'); + + $message = explode( + "\n", PHPUnit_Framework_TestFailure::exceptionToString($e) + ); + + $diagnostic = array( + 'message' => $message[0], + 'severity' => 'fail' + ); + + if ($e instanceof PHPUnit_Framework_ExpectationFailedException) { + $cf = $e->getComparisonFailure(); + + if ($cf !== NULL) { + $diagnostic['data'] = array( + 'got' => $cf->getActual(), + 'expected' => $cf->getExpected() + ); + } + } + + $yaml = new Symfony\Component\Yaml\Dumper; + + $this->write( + sprintf( + " ---\n%s ...\n", + $yaml->dump($diagnostic, 2, 2) + ) + ); + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeNotOk($test, '', 'TODO Incomplete Test'); + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->write( + sprintf( + "ok %d - # SKIP%s\n", + + $this->testNumber, + $e->getMessage() != '' ? ' ' . $e->getMessage() : '' + ) + ); + + $this->testSuccessful = FALSE; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->testSuiteLevel++; + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->testSuiteLevel--; + + if ($this->testSuiteLevel == 0) { + $this->write(sprintf("1..%d\n", $this->testNumber)); + } + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $this->testNumber++; + $this->testSuccessful = TRUE; + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if ($this->testSuccessful === TRUE) { + $this->write( + sprintf( + "ok %d - %s\n", + + $this->testNumber, + PHPUnit_Util_Test::describe($test) + ) + ); + } + } + + /** + * @param PHPUnit_Framework_Test $test + * @param string $prefix + * @param string $directive + */ + protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '') + { + $this->write( + sprintf( + "not ok %d - %s%s%s\n", + + $this->testNumber, + $prefix != '' ? $prefix . ': ' : '', + PHPUnit_Util_Test::describe($test), + $directive != '' ? ' # ' . $directive : '' + ) + ); + + $this->testSuccessful = FALSE; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/PHP.php b/vendor/phpunit/phpunit/PHPUnit/Util/PHP.php new file mode 100644 index 0000000..7c75cf1 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/PHP.php @@ -0,0 +1,332 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.4.0 + */ + +/** + * Utility methods for PHP sub-processes. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.4.0 + */ +abstract class PHPUnit_Util_PHP +{ + /** + * @var string $phpBinary + */ + protected $phpBinary; + + /** + * Returns the path to a PHP interpreter. + * + * PHPUnit_Util_PHP::$phpBinary contains the path to the PHP + * interpreter. + * + * When not set, the following assumptions will be made: + * + * 1. When PHPUnit is run using the CLI SAPI and the $_SERVER['_'] + * variable does not contain the string "PHPUnit", $_SERVER['_'] + * is assumed to contain the path to the current PHP interpreter + * and that will be used. + * + * 2. When PHPUnit is run using the CLI SAPI and the $_SERVER['_'] + * variable contains the string "PHPUnit", the file that $_SERVER['_'] + * points to is assumed to be the PHPUnit TextUI CLI wrapper script + * "phpunit" and the binary set up using #! on that file's first + * line of code is assumed to contain the path to the current PHP + * interpreter and that will be used. + * + * 3. When the PHP CLI/CGI binary configured with the PEAR Installer + * (php_bin configuration value) is readable, it will be used. + * + * 4. The current PHP interpreter is assumed to be in the $PATH and + * to be invokable through "php". + * + * @return string + */ + protected function getPhpBinary() + { + if ($this->phpBinary === NULL) { + if (defined("PHP_BINARY")) { + $this->phpBinary = PHP_BINARY; + } else if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) { + if (strpos($_SERVER['_'], 'phpunit') !== FALSE) { + $file = file($_SERVER['_']); + + if (strpos($file[0], ' ') !== FALSE) { + $tmp = explode(' ', $file[0]); + $this->phpBinary = trim($tmp[1]); + } else { + $this->phpBinary = ltrim(trim($file[0]), '#!'); + } + } else if (strpos(basename($_SERVER['_']), 'php') !== FALSE) { + $this->phpBinary = $_SERVER['_']; + } + } + + if ($this->phpBinary === NULL) { + $possibleBinaryLocations = array( + PHP_BINDIR . '/php', + PHP_BINDIR . '/php-cli.exe', + PHP_BINDIR . '/php.exe', + '@php_bin@', + ); + foreach ($possibleBinaryLocations as $binary) { + if (is_readable($binary)) { + $this->phpBinary = $binary; + break; + } + } + } + + if (!is_readable($this->phpBinary)) { + $this->phpBinary = 'php'; + } else { + $this->phpBinary = escapeshellcmd($this->phpBinary); + } + } + + return $this->phpBinary; + } + + /** + * @return PHPUnit_Util_PHP + * @since Method available since Release 3.5.12 + */ + public static function factory() + { + if (DIRECTORY_SEPARATOR == '\\') { + return new PHPUnit_Util_PHP_Windows; + } + + return new PHPUnit_Util_PHP_Default; + } + + /** + * Runs a single job (PHP code) using a separate PHP process. + * + * @param string $job + * @param PHPUnit_Framework_TestCase $test + * @param PHPUnit_Framework_TestResult $result + * @return array|null + * @throws PHPUnit_Framework_Exception + */ + public function runJob($job, PHPUnit_Framework_Test $test = NULL, PHPUnit_Framework_TestResult $result = NULL) + { + $process = proc_open( + $this->getPhpBinary(), + array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ), + $pipes + ); + + if (!is_resource($process)) { + throw new PHPUnit_Framework_Exception( + 'Unable to create process for process isolation.' + ); + } + + if ($result !== NULL) { + $result->startTest($test); + } + + $this->process($pipes[0], $job); + fclose($pipes[0]); + + $stdout = stream_get_contents($pipes[1]); + fclose($pipes[1]); + + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[2]); + + proc_close($process); + $this->cleanup(); + + if ($result !== NULL) { + $this->processChildResult($test, $result, $stdout, $stderr); + } else { + return array('stdout' => $stdout, 'stderr' => $stderr); + } + } + + /** + * @param resource $pipe + * @param string $job + * @since Method available since Release 3.5.12 + */ + abstract protected function process($pipe, $job); + + /** + * @since Method available since Release 3.5.12 + */ + protected function cleanup() + { + } + + /** + * Processes the TestResult object from an isolated process. + * + * @param PHPUnit_Framework_TestCase $test + * @param PHPUnit_Framework_TestResult $result + * @param string $stdout + * @param string $stderr + * @since Method available since Release 3.5.0 + */ + protected function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr) + { + if (!empty($stderr)) { + $time = 0; + $result->addError( + $test, + new PHPUnit_Framework_Exception(trim($stderr)), $time + ); + } else { + set_error_handler(function($errno, $errstr, $errfile, $errline) { + throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); + }); + try { + $childResult = unserialize($stdout); + restore_error_handler(); + } catch (ErrorException $e) { + restore_error_handler(); + $childResult = FALSE; + + $time = 0; + $result->addError( + $test, new PHPUnit_Framework_Exception(trim($stdout), 0, $e), $time + ); + } + + if ($childResult !== FALSE) { + if (!empty($childResult['output'])) { + print $childResult['output']; + } + + $test->setResult($childResult['testResult']); + $test->addToAssertionCount($childResult['numAssertions']); + + $childResult = $childResult['result']; + + if ($result->getCollectCodeCoverageInformation()) { + $result->getCodeCoverage()->merge( + $childResult->getCodeCoverage() + ); + } + + $time = $childResult->time(); + $notImplemented = $childResult->notImplemented(); + $skipped = $childResult->skipped(); + $errors = $childResult->errors(); + $failures = $childResult->failures(); + + if (!empty($notImplemented)) { + $result->addError( + $test, $this->getException($notImplemented[0]), $time + ); + } + + else if (!empty($skipped)) { + $result->addError( + $test, $this->getException($skipped[0]), $time + ); + } + + else if (!empty($errors)) { + $result->addError( + $test, $this->getException($errors[0]), $time + ); + } + + else if (!empty($failures)) { + $result->addFailure( + $test, $this->getException($failures[0]), $time + ); + } + } + } + + $result->endTest($test, $time); + } + + /** + * Gets the thrown exception from a PHPUnit_Framework_TestFailure. + * + * @param PHPUnit_Framework_TestFailure $error + * @since Method available since Release 3.6.0 + * @see https://github.com/sebastianbergmann/phpunit/issues/74 + */ + protected function getException(PHPUnit_Framework_TestFailure $error) + { + $exception = $error->thrownException(); + + if ($exception instanceof __PHP_Incomplete_Class) { + $exceptionArray = array(); + foreach ((array)$exception as $key => $value) { + $key = substr($key, strrpos($key, "\0") + 1); + $exceptionArray[$key] = $value; + } + + $exception = new PHPUnit_Framework_SyntheticError( + sprintf( + '%s: %s', + $exceptionArray['_PHP_Incomplete_Class_Name'], + $exceptionArray['message'] + ), + $exceptionArray['code'], + $exceptionArray['file'], + $exceptionArray['line'], + $exceptionArray['trace'] + ); + } + + return $exception; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Default.php b/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Default.php new file mode 100644 index 0000000..f6b08aa --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Default.php @@ -0,0 +1,67 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.12 + */ + +/** + * Default utility for PHP sub-processes. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.12 + */ +class PHPUnit_Util_PHP_Default extends PHPUnit_Util_PHP +{ + /** + * @param resource $pipe + * @since Method available since Release 3.5.12 + */ + protected function process($pipe, $job) + { + fwrite($pipe, $job); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Windows.php b/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Windows.php new file mode 100644 index 0000000..d682b89 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/PHP/Windows.php @@ -0,0 +1,90 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.12 + */ + +/** + * Windows utility for PHP sub-processes. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.12 + */ +class PHPUnit_Util_PHP_Windows extends PHPUnit_Util_PHP +{ + /** + * @var string + */ + protected $tempFile; + + /** + * @param resource $pipe + * @since Method available since Release 3.5.12 + */ + protected function process($pipe, $job) + { + if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) || + file_put_contents($this->tempFile, $job) === FALSE) { + throw new PHPUnit_Framework_Exception( + 'Unable to write temporary files for process isolation.' + ); + } + + fwrite( + $pipe, + "tempFile, TRUE) . "; ?>" + ); + } + + /** + * @since Method available since Release 3.5.12 + */ + protected function cleanup() + { + unlink($this->tempFile); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Printer.php b/vendor/phpunit/phpunit/PHPUnit/Util/Printer.php new file mode 100644 index 0000000..e59c2ce --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Printer.php @@ -0,0 +1,208 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +/** + * Utility class that can print to STDOUT or write to a file. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Util_Printer +{ + /** + * If TRUE, flush output after every write. + * + * @var boolean + */ + protected $autoFlush = FALSE; + + /** + * @var resource + */ + protected $out; + + /** + * @var string + */ + protected $outTarget; + + /** + * @var boolean + */ + protected $printsHTML = FALSE; + + /** + * Constructor. + * + * @param mixed $out + * @throws PHPUnit_Framework_Exception + */ + public function __construct($out = NULL) + { + if ($out !== NULL) { + if (is_string($out)) { + if (strpos($out, 'socket://') === 0) { + $out = explode(':', str_replace('socket://', '', $out)); + + if (sizeof($out) != 2) { + throw new PHPUnit_Framework_Exception; + } + + $this->out = fsockopen($out[0], $out[1]); + } else { + if (strpos($out, 'php://') === FALSE && + !is_dir(dirname($out))) { + mkdir(dirname($out), 0777, TRUE); + } + + $this->out = fopen($out, 'wt'); + } + + $this->outTarget = $out; + } else { + $this->out = $out; + } + } + } + + /** + * Flush buffer, optionally tidy up HTML, and close output if it's not to a php stream + */ + public function flush() + { + if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) { + fclose($this->out); + } + + if ($this->printsHTML === TRUE && + $this->outTarget !== NULL && + strpos($this->outTarget, 'php://') !== 0 && + strpos($this->outTarget, 'socket://') !== 0 && + extension_loaded('tidy')) { + file_put_contents( + $this->outTarget, + tidy_repair_file( + $this->outTarget, array('indent' => TRUE, 'wrap' => 0), 'utf8' + ) + ); + } + } + + /** + * Performs a safe, incremental flush. + * + * Do not confuse this function with the flush() function of this class, + * since the flush() function may close the file being written to, rendering + * the current object no longer usable. + * + * @since Method available since Release 3.3.0 + */ + public function incrementalFlush() + { + if ($this->out) { + fflush($this->out); + } else { + flush(); + } + } + + /** + * @param string $buffer + */ + public function write($buffer) + { + if ($this->out) { + fwrite($this->out, $buffer); + + if ($this->autoFlush) { + $this->incrementalFlush(); + } + } else { + if (PHP_SAPI != 'cli') { + $buffer = htmlspecialchars($buffer); + } + + print $buffer; + + if ($this->autoFlush) { + $this->incrementalFlush(); + } + } + } + + /** + * Check auto-flush mode. + * + * @return boolean + * @since Method available since Release 3.3.0 + */ + public function getAutoFlush() + { + return $this->autoFlush; + } + + /** + * Set auto-flushing mode. + * + * If set, *incremental* flushes will be done after each write. This should + * not be confused with the different effects of this class' flush() method. + * + * @param boolean $autoFlush + * @since Method available since Release 3.3.0 + */ + public function setAutoFlush($autoFlush) + { + if (is_bool($autoFlush)) { + $this->autoFlush = $autoFlush; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/String.php b/vendor/phpunit/phpunit/PHPUnit/Util/String.php new file mode 100644 index 0000000..c442fdd --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/String.php @@ -0,0 +1,118 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * String helpers. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Util_String +{ + /** + * Converts a string to UTF-8 encoding. + * + * @param string $string + * @return string + */ + public static function convertToUtf8($string) + { + if (!self::isUtf8($string)) { + if (function_exists('mb_convert_encoding')) { + $string = mb_convert_encoding($string, 'UTF-8'); + } else { + $string = utf8_encode($string); + } + } + + return $string; + } + + /** + * Checks a string for UTF-8 encoding. + * + * @param string $string + * @return boolean + */ + protected static function isUtf8($string) + { + $length = strlen($string); + + for ($i = 0; $i < $length; $i++) { + if (ord($string[$i]) < 0x80) { + $n = 0; + } + + else if ((ord($string[$i]) & 0xE0) == 0xC0) { + $n = 1; + } + + else if ((ord($string[$i]) & 0xF0) == 0xE0) { + $n = 2; + } + + else if ((ord($string[$i]) & 0xF0) == 0xF0) { + $n = 3; + } + + else { + return FALSE; + } + + for ($j = 0; $j < $n; $j++) { + if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80)) { + return FALSE; + } + } + } + + return TRUE; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Test.php b/vendor/phpunit/phpunit/PHPUnit/Util/Test.php new file mode 100644 index 0000000..ea11d2d --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Test.php @@ -0,0 +1,601 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Test helpers. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Test +{ + const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/'; + const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m'; + const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; + const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s(?P([^ ]+))\r?$/m'; + + const SMALL = 0; + const MEDIUM = 1; + const LARGE = 2; + + private static $annotationCache = array(); + + protected static $templateMethods = array( + 'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown' + ); + + /** + * @param PHPUnit_Framework_Test $test + * @param boolean $asString + * @return mixed + */ + public static function describe(PHPUnit_Framework_Test $test, $asString = TRUE) + { + if ($asString) { + if ($test instanceof PHPUnit_Framework_SelfDescribing) { + return $test->toString(); + } else { + return get_class($test); + } + } else { + if ($test instanceof PHPUnit_Framework_TestCase) { + return array( + get_class($test), $test->getName() + ); + } + + else if ($test instanceof PHPUnit_Framework_SelfDescribing) { + return array('', $test->toString()); + } + + else { + return array('', get_class($test)); + } + } + } + + /** + * Returns the requirements for a test. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 3.6.0 + */ + public static function getRequirements($className, $methodName) + { + $reflector = new ReflectionClass($className); + $docComment = $reflector->getDocComment(); + $reflector = new ReflectionMethod($className, $methodName); + $docComment .= "\n" . $reflector->getDocComment(); + $requires = array(); + + if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) { + for ($i = 0; $i < $count; $i++) { + $requires[$matches['name'][$i]] = $matches['value'][$i]; + } + } + if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) { + for ($i = 0; $i < $count; $i++) { + $name = $matches['name'][$i] . 's'; + if (!isset($requires[$name])) { + $requires[$name] = array(); + } + $requires[$name][] = $matches['value'][$i]; + } + } + + return $requires; + } + + /** + * Returns the expected exception for a test. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 3.3.6 + */ + public static function getExpectedException($className, $methodName) + { + $reflector = new ReflectionMethod($className, $methodName); + $docComment = $reflector->getDocComment(); + + if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) { + $annotations = self::parseTestMethodAnnotations( + $className, $methodName + ); + + $class = $matches[1]; + $code = NULL; + $message = ''; + + if (isset($matches[2])) { + $message = trim($matches[2]); + } + + else if (isset($annotations['method']['expectedExceptionMessage'])) { + $message = self::_parseAnnotationContent( + $annotations['method']['expectedExceptionMessage'][0] + ); + } + + if (isset($matches[3])) { + $code = $matches[3]; + } + + else if (isset($annotations['method']['expectedExceptionCode'])) { + $code = self::_parseAnnotationContent( + $annotations['method']['expectedExceptionCode'][0] + ); + } + + if (is_numeric($code)) { + $code = (int)$code; + } + + else if (is_string($code) && defined($code)) { + $code = (int)constant($code); + } + + return array( + 'class' => $class, 'code' => $code, 'message' => $message + ); + } + + return FALSE; + } + + /** + * Parse annotation content to use constant/class constant values + * + * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME + * + * If the constant is not found the string is used as is to ensure maximum BC. + * + * @param string $message + * @return string + */ + protected static function _parseAnnotationContent($message) + { + if (strpos($message, '::') !== FALSE && count(explode('::', $message) == 2)) { + if (defined($message)) { + $message = constant($message); + } + } + return $message; + } + + /** + * Returns the provided data for a method. + * + * @param string $className + * @param string $methodName + * @param string $docComment + * @return mixed array|Iterator when a data provider is specified and exists + * false when a data provider is specified and does not exist + * null when no data provider is specified + * @since Method available since Release 3.2.0 + */ + public static function getProvidedData($className, $methodName) + { + $reflector = new ReflectionMethod($className, $methodName); + $docComment = $reflector->getDocComment(); + $data = NULL; + + if (preg_match(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { + $dataProviderMethodNameNamespace = explode('\\', $matches[1]); + $leaf = explode('::', array_pop($dataProviderMethodNameNamespace)); + $dataProviderMethodName = array_pop($leaf); + + if (!empty($dataProviderMethodNameNamespace)) { + $dataProviderMethodNameNamespace = join('\\', $dataProviderMethodNameNamespace) . '\\'; + } else { + $dataProviderMethodNameNamespace = ''; + } + + if (!empty($leaf)) { + $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf); + } else { + $dataProviderClassName = $className; + } + + $dataProviderClass = new ReflectionClass($dataProviderClassName); + $dataProviderMethod = $dataProviderClass->getMethod( + $dataProviderMethodName + ); + + if ($dataProviderMethod->isStatic()) { + $object = NULL; + } else { + $object = $dataProviderClass->newInstance(); + } + + if ($dataProviderMethod->getNumberOfParameters() == 0) { + $data = $dataProviderMethod->invoke($object); + } else { + $data = $dataProviderMethod->invoke($object, $methodName); + } + } + + if ($data !== NULL) { + foreach ($data as $key => $value) { + if (!is_array($value)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Data set %s is invalid.', + is_int($key) ? '#' . $key : '"' . $key . '"' + ) + ); + } + } + } + + return $data; + } + + /** + * @param string $className + * @param string $methodName + * @return array + * @throws ReflectionException + * @since Method available since Release 3.4.0 + */ + public static function parseTestMethodAnnotations($className, $methodName = '') + { + if (!isset(self::$annotationCache[$className])) { + $class = new ReflectionClass($className); + self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment()); + } + + if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) { + try { + $method = new ReflectionMethod($className, $methodName); + $annotations = self::parseAnnotations($method->getDocComment()); + } catch (ReflectionException $e) { + $annotations = array(); + } + self::$annotationCache[$className . '::' . $methodName] = $annotations; + } + + return array( + 'class' => self::$annotationCache[$className], + 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : array() + ); + } + + /** + * @param string $docblock + * @return array + * @since Method available since Release 3.4.0 + */ + private static function parseAnnotations($docblock) + { + $annotations = array(); + // Strip away the docblock header and footer to ease parsing of one line annotations + $docblock = substr($docblock, 3, -2); + + if (preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docblock, $matches)) { + $numMatches = count($matches[0]); + + for ($i = 0; $i < $numMatches; ++$i) { + $annotations[$matches['name'][$i]][] = $matches['value'][$i]; + } + } + + return $annotations; + } + + /** + * Returns the backup settings for a test. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 3.4.0 + */ + public static function getBackupSettings($className, $methodName) + { + return array( + 'backupGlobals' => self::getBooleanAnnotationSetting( + $className, $methodName, 'backupGlobals' + ), + 'backupStaticAttributes' => self::getBooleanAnnotationSetting( + $className, $methodName, 'backupStaticAttributes' + ) + ); + } + + /** + * Returns the dependencies for a test class or method. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 3.4.0 + */ + public static function getDependencies($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, $methodName + ); + + $dependencies = array(); + + if (isset($annotations['class']['depends'])) { + $dependencies = $annotations['class']['depends']; + } + + if (isset($annotations['method']['depends'])) { + $dependencies = array_merge( + $dependencies, $annotations['method']['depends'] + ); + } + + return array_unique($dependencies); + } + + /** + * Returns the error handler settings for a test. + * + * @param string $className + * @param string $methodName + * @return boolean + * @since Method available since Release 3.4.0 + */ + public static function getErrorHandlerSettings($className, $methodName) + { + return self::getBooleanAnnotationSetting( + $className, $methodName, 'errorHandler' + ); + } + + /** + * Returns the groups for a test class or method. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 3.2.0 + */ + public static function getGroups($className, $methodName = '') + { + $annotations = self::parseTestMethodAnnotations( + $className, $methodName + ); + + $groups = array(); + + if (isset($annotations['method']['author'])) { + $groups = $annotations['method']['author']; + } + + else if (isset($annotations['class']['author'])) { + $groups = $annotations['class']['author']; + } + + if (isset($annotations['class']['group'])) { + $groups = array_merge($groups, $annotations['class']['group']); + } + + if (isset($annotations['method']['group'])) { + $groups = array_merge($groups, $annotations['method']['group']); + } + + if (isset($annotations['class']['ticket'])) { + $groups = array_merge($groups, $annotations['class']['ticket']); + } + + if (isset($annotations['method']['ticket'])) { + $groups = array_merge($groups, $annotations['method']['ticket']); + } + + foreach (array('small', 'medium', 'large') as $size) { + if (isset($annotations['method'][$size])) { + $groups[] = $size; + } + + else if (isset($annotations['class'][$size])) { + $groups[] = $size; + } + } + + return array_unique($groups); + } + + /** + * Returns the size of the test. + * + * @param string $className + * @param string $methodName + * @return integer + * @since Method available since Release 3.6.0 + */ + public static function getSize($className, $methodName) + { + $groups = array_flip(self::getGroups($className, $methodName)); + $size = self::SMALL; + $class = new ReflectionClass($className); + + if ((class_exists('PHPUnit_Extensions_Database_TestCase', FALSE) && + $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase')) || + (class_exists('PHPUnit_Extensions_SeleniumTestCase', FALSE) && + $class->isSubclassOf('PHPUnit_Extensions_SeleniumTestCase'))) { + $size = self::LARGE; + } + + else if (isset($groups['medium'])) { + $size = self::MEDIUM; + } + + else if (isset($groups['large'])) { + $size = self::LARGE; + } + + return $size; + } + + /** + * Returns the tickets for a test class or method. + * + * @param string $className + * @param string $methodName + * @return array + * @since Method available since Release 3.4.0 + */ + public static function getTickets($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, $methodName + ); + + $tickets = array(); + + if (isset($annotations['class']['ticket'])) { + $tickets = $annotations['class']['ticket']; + } + + if (isset($annotations['method']['ticket'])) { + $tickets = array_merge($tickets, $annotations['method']['ticket']); + } + + return array_unique($tickets); + } + + /** + * Returns the output buffering settings for a test. + * + * @param string $className + * @param string $methodName + * @return boolean + * @since Method available since Release 3.4.0 + */ + public static function getOutputBufferingSettings($className, $methodName) + { + return self::getBooleanAnnotationSetting( + $className, $methodName, 'outputBuffering' + ); + } + + /** + * Returns the process isolation settings for a test. + * + * @param string $className + * @param string $methodName + * @return boolean + * @since Method available since Release 3.4.1 + */ + public static function getProcessIsolationSettings($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, $methodName + ); + + if (isset($annotations['class']['runTestsInSeparateProcesses']) || + isset($annotations['method']['runInSeparateProcess'])) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * Returns the preserve global state settings for a test. + * + * @param string $className + * @param string $methodName + * @return boolean + * @since Method available since Release 3.4.0 + */ + public static function getPreserveGlobalStateSettings($className, $methodName) + { + return self::getBooleanAnnotationSetting( + $className, $methodName, 'preserveGlobalState' + ); + } + + /** + * @param string $className + * @param string $methodName + * @param string $settingName + * @return boolean + * @since Method available since Release 3.4.0 + */ + private static function getBooleanAnnotationSetting($className, $methodName, $settingName) + { + $annotations = self::parseTestMethodAnnotations( + $className, $methodName + ); + + $result = NULL; + + if (isset($annotations['class'][$settingName])) { + if ($annotations['class'][$settingName][0] == 'enabled') { + $result = TRUE; + } + + else if ($annotations['class'][$settingName][0] == 'disabled') { + $result = FALSE; + } + } + + if (isset($annotations['method'][$settingName])) { + if ($annotations['method'][$settingName][0] == 'enabled') { + $result = TRUE; + } + + else if ($annotations['method'][$settingName][0] == 'disabled') { + $result = FALSE; + } + } + + return $result; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/NamePrettifier.php b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/NamePrettifier.php new file mode 100644 index 0000000..37ae2b4 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/NamePrettifier.php @@ -0,0 +1,177 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * Prettifies class and method names for use in TestDox documentation. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_TestDox_NamePrettifier +{ + /** + * @var string + */ + protected $prefix = 'Test'; + + /** + * @var string + */ + protected $suffix = 'Test'; + + /** + * @var array + */ + protected $strings = array(); + + /** + * Prettifies the name of a test class. + * + * @param string $name + * @return string + */ + public function prettifyTestClass($name) + { + $title = $name; + + if ($this->suffix !== NULL && + $this->suffix == substr($name, -1 * strlen($this->suffix))) { + $title = substr($title, 0, strripos($title, $this->suffix)); + } + + if ($this->prefix !== NULL && + $this->prefix == substr($name, 0, strlen($this->prefix))) { + $title = substr($title, strlen($this->prefix)); + } + + return $title; + } + + /** + * Prettifies the name of a test method. + * + * @param string $name + * @return string + */ + public function prettifyTestMethod($name) + { + $buffer = ''; + + if (!is_string($name) || strlen($name) == 0) { + return $buffer; + } + + $string = preg_replace('#\d+$#', '', $name, -1, $count); + + if (in_array($string, $this->strings)) { + $name = $string; + } else if ($count == 0) { + $this->strings[] = $string; + } + + if (strpos($name, '_') !== FALSE) { + return str_replace('_', ' ', $name); + } + + $max = strlen($name); + + if (substr($name, 0, 4) == 'test') { + $offset = 4; + } else { + $offset = 0; + $name[0] = strtoupper($name[0]); + } + + $wasNumeric = FALSE; + + for ($i = $offset; $i < $max; $i++) { + if ($i > $offset && + ord($name[$i]) >= 65 && + ord($name[$i]) <= 90) { + $buffer .= ' ' . strtolower($name[$i]); + } else { + $isNumeric = is_numeric($name[$i]); + + if (!$wasNumeric && $isNumeric) { + $buffer .= ' '; + $wasNumeric = TRUE; + } + + if ($wasNumeric && !$isNumeric) { + $wasNumeric = FALSE; + } + + $buffer .= $name[$i]; + } + } + + return $buffer; + } + + /** + * Sets the prefix of test names. + * + * @param string $prefix + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Sets the suffix of test names. + * + * @param string $prefix + */ + public function setSuffix($suffix) + { + $this->suffix = $suffix; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter.php b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter.php new file mode 100644 index 0000000..6a47c22 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter.php @@ -0,0 +1,347 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * Base class for printers of TestDox documentation. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.1.0 + */ +abstract class PHPUnit_Util_TestDox_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var PHPUnit_Util_TestDox_NamePrettifier + */ + protected $prettifier; + + /** + * @var string + */ + protected $testClass = ''; + + /** + * @var integer + */ + protected $testStatus = FALSE; + + /** + * @var array + */ + protected $tests = array(); + + /** + * @var integer + */ + protected $successful = 0; + + /** + * @var integer + */ + protected $failed = 0; + + /** + * @var integer + */ + protected $skipped = 0; + + /** + * @var integer + */ + protected $incomplete = 0; + + /** + * @var string + */ + protected $testTypeOfInterest = 'PHPUnit_Framework_TestCase'; + + /** + * @var string + */ + protected $currentTestClassPrettified; + + /** + * @var string + */ + protected $currentTestMethodPrettified; + + /** + * Constructor. + * + * @param resource $out + */ + public function __construct($out = NULL) + { + parent::__construct($out); + + $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier; + $this->startRun(); + } + + /** + * Flush buffer and close output. + * + */ + public function flush() + { + $this->doEndClass(); + $this->endRun(); + + parent::flush(); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($test instanceof $this->testTypeOfInterest) { + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; + $this->failed++; + } + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if ($test instanceof $this->testTypeOfInterest) { + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; + $this->failed++; + } + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($test instanceof $this->testTypeOfInterest) { + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE; + $this->incomplete++; + } + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($test instanceof $this->testTypeOfInterest) { + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED; + $this->skipped++; + } + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if ($test instanceof $this->testTypeOfInterest) { + $class = get_class($test); + + if ($this->testClass != $class) { + if ($this->testClass != '') { + $this->doEndClass(); + } + + $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); + $this->startClass($class); + + $this->testClass = $class; + $this->tests = array(); + } + + $prettified = FALSE; + + if ($test instanceof PHPUnit_Framework_TestCase && + !$test instanceof PHPUnit_Framework_Warning) { + $annotations = $test->getAnnotations(); + + if (isset($annotations['method']['testdox'][0])) { + $this->currentTestMethodPrettified = $annotations['method']['testdox'][0]; + $prettified = TRUE; + } + } + + if (!$prettified) { + $this->currentTestMethodPrettified = $this->prettifier->prettifyTestMethod($test->getName(FALSE)); + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED; + } + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if ($test instanceof $this->testTypeOfInterest) { + if (!isset($this->tests[$this->currentTestMethodPrettified])) { + if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + $this->tests[$this->currentTestMethodPrettified]['success'] = 1; + $this->tests[$this->currentTestMethodPrettified]['failure'] = 0; + } else { + $this->tests[$this->currentTestMethodPrettified]['success'] = 0; + $this->tests[$this->currentTestMethodPrettified]['failure'] = 1; + } + } else { + if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + $this->tests[$this->currentTestMethodPrettified]['success']++; + } else { + $this->tests[$this->currentTestMethodPrettified]['failure']++; + } + } + + $this->currentTestClassPrettified = NULL; + $this->currentTestMethodPrettified = NULL; + } + } + + /** + * @since Method available since Release 2.3.0 + */ + protected function doEndClass() + { + foreach ($this->tests as $name => $data) { + $this->onTest($name, $data['failure'] == 0); + } + + $this->endClass($this->testClass); + } + + /** + * Handler for 'start run' event. + * + */ + protected function startRun() + { + } + + /** + * Handler for 'start class' event. + * + * @param string $name + */ + protected function startClass($name) + { + } + + /** + * Handler for 'on test' event. + * + * @param string $name + * @param boolean $success + */ + protected function onTest($name, $success = TRUE) + { + } + + /** + * Handler for 'end class' event. + * + * @param string $name + */ + protected function endClass($name) + { + } + + /** + * Handler for 'end run' event. + * + */ + protected function endRun() + { + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php new file mode 100644 index 0000000..5c9d6d0 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php @@ -0,0 +1,123 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * Prints TestDox documentation in HTML format. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_TestDox_ResultPrinter_HTML extends PHPUnit_Util_TestDox_ResultPrinter +{ + /** + * @var boolean + */ + protected $printsHTML = TRUE; + + /** + * Handler for 'start run' event. + * + */ + protected function startRun() + { + $this->write(''); + } + + /** + * Handler for 'start class' event. + * + * @param string $name + */ + protected function startClass($name) + { + $this->write( + '

      ' . $this->currentTestClassPrettified . + '

        ' + ); + } + + /** + * Handler for 'on test' event. + * + * @param string $name + * @param boolean $success + */ + protected function onTest($name, $success = TRUE) + { + if (!$success) { + $strikeOpen = ''; + $strikeClose = ''; + } else { + $strikeOpen = ''; + $strikeClose = ''; + } + + $this->write('
      • ' . $strikeOpen . $name . $strikeClose . '
      • '); + } + + /** + * Handler for 'end class' event. + * + * @param string $name + */ + protected function endClass($name) + { + $this->write('
      '); + } + + /** + * Handler for 'end run' event. + * + */ + protected function endRun() + { + $this->write(''); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/Text.php b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/Text.php new file mode 100644 index 0000000..88868dd --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/Text.php @@ -0,0 +1,95 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * Prints TestDox documentation in text format. + * + * @package PHPUnit + * @subpackage Util_TestDox + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_TestDox_ResultPrinter_Text extends PHPUnit_Util_TestDox_ResultPrinter +{ + /** + * Handler for 'start class' event. + * + * @param string $name + */ + protected function startClass($name) + { + $this->write($this->currentTestClassPrettified . "\n"); + } + + /** + * Handler for 'on test' event. + * + * @param string $name + * @param boolean $success + */ + protected function onTest($name, $success = TRUE) + { + if ($success) { + $this->write(' [x] '); + } else { + $this->write(' [ ] '); + } + + $this->write($name . "\n"); + } + + /** + * Handler for 'end class' event. + * + * @param string $name + */ + protected function endClass($name) + { + $this->write("\n"); + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/TestSuiteIterator.php b/vendor/phpunit/phpunit/PHPUnit/Util/TestSuiteIterator.php new file mode 100644 index 0000000..1407f7f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/TestSuiteIterator.php @@ -0,0 +1,148 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.1.0 + */ + +/** + * Iterator for test suites. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Util_TestSuiteIterator implements RecursiveIterator +{ + /** + * @var integer + */ + protected $position; + + /** + * @var PHPUnit_Framework_Test[] + */ + protected $tests; + + /** + * Constructor. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function __construct(PHPUnit_Framework_TestSuite $testSuite) + { + $this->tests = $testSuite->tests(); + } + + /** + * Rewinds the Iterator to the first element. + * + */ + public function rewind() + { + $this->position = 0; + } + + /** + * Checks if there is a current element after calls to rewind() or next(). + * + * @return boolean + */ + public function valid() + { + return $this->position < count($this->tests); + } + + /** + * Returns the key of the current element. + * + * @return integer + */ + public function key() + { + return $this->position; + } + + /** + * Returns the current element. + * + * @return PHPUnit_Framework_Test + */ + public function current() + { + return $this->valid() ? $this->tests[$this->position] : NULL; + } + + /** + * Moves forward to next element. + * + */ + public function next() + { + $this->position++; + } + + /** + * Returns the sub iterator for the current element. + * + * @return PHPUnit_Util_TestSuiteIterator + */ + public function getChildren() + { + return new PHPUnit_Util_TestSuiteIterator( + $this->tests[$this->position] + ); + } + + /** + * Checks whether the current element has children. + * + * @return boolean + */ + public function hasChildren() + { + return $this->tests[$this->position] instanceof PHPUnit_Framework_TestSuite; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/Type.php b/vendor/phpunit/phpunit/PHPUnit/Util/Type.php new file mode 100644 index 0000000..9da081f --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/Type.php @@ -0,0 +1,303 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * Utility class for textual type (and value) representation. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Type +{ + public static function isType($type) + { + return in_array( + $type, + array( + 'numeric', + 'integer', + 'int', + 'float', + 'string', + 'boolean', + 'bool', + 'null', + 'array', + 'object', + 'resource', + 'scalar' + ) + ); + } + + /** + * Exports a value into a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @return string + * @since Method available since Release 3.6.0 + */ + public static function export($value, $indentation = 0) + { + return self::recursiveExport($value, $indentation); + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @param array $processedObjects Contains all objects that were already + * rendered + * @return string + * @since Method available since Release 3.6.0 + * @see PHPUnit_Util_Type::export + */ + protected static function recursiveExport($value, $indentation, &$processedObjects = array()) + { + if ($value === NULL) { + return 'null'; + } + + if ($value === TRUE) { + return 'true'; + } + + if ($value === FALSE) { + return 'false'; + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . + "'"; + } + + $origValue = $value; + + if (is_object($value)) { + if (in_array($value, $processedObjects, TRUE)) { + return sprintf( + '%s Object (*RECURSION*)', + + get_class($value) + ); + } + + $processedObjects[] = $value; + + // Convert object to array + $value = self::toArray($value); + } + + if (is_array($value)) { + $whitespace = str_repeat(' ', $indentation); + + // There seems to be no other way to check arrays for recursion + // http://www.php.net/manual/en/language.types.array.php#73936 + preg_match_all('/\n \[(\w+)\] => Array\s+\*RECURSION\*/', print_r($value, TRUE), $matches); + $recursiveKeys = array_unique($matches[1]); + + // Convert to valid array keys + // Numeric integer strings are automatically converted to integers + // by PHP + foreach ($recursiveKeys as $key => $recursiveKey) { + if ((string)(integer)$recursiveKey === $recursiveKey) { + $recursiveKeys[$key] = (integer)$recursiveKey; + } + } + + $content = ''; + + foreach ($value as $key => $val) { + if (in_array($key, $recursiveKeys, TRUE)) { + $val = 'Array (*RECURSION*)'; + } + + else { + $val = self::recursiveExport($val, $indentation+1, $processedObjects); + } + + $content .= $whitespace . ' ' . self::export($key) . ' => ' . $val . "\n"; + } + + if (strlen($content) > 0) { + $content = "\n" . $content . $whitespace; + } + + return sprintf( + "%s (%s)", + + is_object($origValue) ? get_class($origValue) . ' Object' : 'Array', + $content + ); + } + + if (is_double($value) && (double)(integer)$value === $value) { + return $value . '.0'; + } + + return (string)$value; + } + + /** + * Exports a value into a single-line string + * + * The output of this method is similar to the output of + * PHPUnit_Util_Type::export. This method guarantees thought that the + * result contains now newlines. + * + * Newlines are replaced by the visible string '\n'. Contents of arrays + * and objects (if any) are replaced by '...'. + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @return string + * @see PHPUnit_Util_Type::export + */ + public static function shortenedExport($value) + { + if (is_string($value)) { + return self::shortenedString($value); + } + + $origValue = $value; + + if (is_object($value)) { + $value = self::toArray($value); + } + + if (is_array($value)) { + return sprintf( + "%s (%s)", + + is_object($origValue) ? get_class($origValue) . ' Object' : 'Array', + count($value) > 0 ? '...' : '' + ); + } + + return self::export($value); + } + + /** + * Shortens a string and converts all new lines to '\n' + * + * @param string $string The string to shorten + * @param integer $max The maximum length for the string + * @return string + */ + public static function shortenedString($string, $maxLength = 40) + { + $string = self::export($string); + + if (strlen($string) > $maxLength) { + $string = substr($string, 0, $maxLength - 10) . '...' . substr($string, -7); + } + + return str_replace("\n", '\n', $string); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + * @since Method available since Release 3.6.0 + */ + public static function toArray($object) + { + $array = array(); + + foreach ((array)$object as $key => $value) { + // properties are transformed to keys in the following way: + + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + $array[$key] = $value; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection + // Format the output similarly to print_r() in this case + if ($object instanceof SplObjectStorage) { + foreach ($object as $key => $value) { + $array[spl_object_hash($value)] = array( + 'obj' => $value, + 'inf' => $object->getInfo(), + ); + } + } + + return $array; + } +} diff --git a/vendor/phpunit/phpunit/PHPUnit/Util/XML.php b/vendor/phpunit/phpunit/PHPUnit/Util/XML.php new file mode 100644 index 0000000..933c493 --- /dev/null +++ b/vendor/phpunit/phpunit/PHPUnit/Util/XML.php @@ -0,0 +1,916 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.2.0 + */ + +/** + * XML helpers. + * + * @package PHPUnit + * @subpackage Util + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.2.0 + */ +class PHPUnit_Util_XML +{ + /** + * @param string $string + * @return string + * @author Kore Nordmann + * @since Method available since Release 3.4.6 + */ + public static function prepareString($string) + { + return preg_replace( + '([\\x00-\\x04\\x0b\\x0c\\x0e-\\x1f\\x7f])e', + 'sprintf( "&#x%02x;", ord( "\\1" ) )', + htmlspecialchars( + PHPUnit_Util_String::convertToUtf8($string), ENT_COMPAT, 'UTF-8' + ) + ); + } + + /** + * Loads an XML (or HTML) file into a DOMDocument object. + * + * @param string $filename + * @param boolean $isHtml + * @param boolean $xinclude + * @return DOMDocument + * @since Method available since Release 3.3.0 + */ + public static function loadFile($filename, $isHtml = FALSE, $xinclude = FALSE) + { + $reporting = error_reporting(0); + $contents = file_get_contents($filename); + error_reporting($reporting); + + if ($contents === FALSE) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Could not read "%s".', + $filename + ) + ); + } + + return self::load($contents, $isHtml, $filename, $xinclude); + } + + /** + * Load an $actual document into a DOMDocument. This is called + * from the selector assertions. + * + * If $actual is already a DOMDocument, it is returned with + * no changes. Otherwise, $actual is loaded into a new DOMDocument + * as either HTML or XML, depending on the value of $isHtml. If $isHtml is + * false and $xinclude is true, xinclude is performed on the loaded + * DOMDocument. + * + * Note: prior to PHPUnit 3.3.0, this method loaded a file and + * not a string as it currently does. To load a file into a + * DOMDocument, use loadFile() instead. + * + * @param string|DOMDocument $actual + * @param boolean $isHtml + * @param string $filename + * @param boolean $xinclude + * @return DOMDocument + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + * @author Tobias Schlitt + */ + public static function load($actual, $isHtml = FALSE, $filename = '', $xinclude = FALSE) + { + if ($actual instanceof DOMDocument) { + return $actual; + } + + $document = new DOMDocument; + + $internal = libxml_use_internal_errors(TRUE); + $message = ''; + $reporting = error_reporting(0); + + if ($isHtml) { + $loaded = $document->loadHTML($actual); + } else { + $loaded = $document->loadXML($actual); + } + + if ('' !== $filename) { + // Necessary for xinclude + $document->documentURI = $filename; + } + + if (!$isHtml && $xinclude) { + $document->xinclude(); + } + + foreach (libxml_get_errors() as $error) { + $message .= $error->message; + } + + libxml_use_internal_errors($internal); + error_reporting($reporting); + + if ($loaded === FALSE) { + if ($filename != '') { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Could not load "%s".%s', + + $filename, + $message != '' ? "\n" . $message : '' + ) + ); + } else { + throw new PHPUnit_Framework_Exception($message); + } + } + + return $document; + } + + /** + * + * + * @param DOMNode $node + * @return string + * @since Method available since Release 3.4.0 + */ + public static function nodeToText(DOMNode $node) + { + if ($node->childNodes->length == 1) { + return $node->nodeValue; + } + + $result = ''; + + foreach ($node->childNodes as $childNode) { + $result .= $node->ownerDocument->saveXML($childNode); + } + + return $result; + } + + /** + * + * + * @param DOMNode $node + * @since Method available since Release 3.3.0 + * @author Mattis Stordalen Flister + */ + public static function removeCharacterDataNodes(DOMNode $node) + { + if ($node->hasChildNodes()) { + for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { + if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { + $node->removeChild($child); + } + } + } + } + + /** + * "Convert" a DOMElement object into a PHP variable. + * + * @param DOMElement $element + * @return mixed + * @since Method available since Release 3.4.0 + */ + public static function xmlToVariable(DOMElement $element) + { + $variable = NULL; + + switch ($element->tagName) { + case 'array': { + $variable = array(); + + foreach ($element->getElementsByTagName('element') as $element) { + $value = self::xmlToVariable($element->childNodes->item(1)); + + if ($element->hasAttribute('key')) { + $variable[(string)$element->getAttribute('key')] = $value; + } else { + $variable[] = $value; + } + } + } + break; + + case 'object': { + $className = $element->getAttribute('class'); + + if ($element->hasChildNodes()) { + $arguments = $element->childNodes->item(1)->childNodes; + $constructorArgs = array(); + + foreach ($arguments as $argument) { + if ($argument instanceof DOMElement) { + $constructorArgs[] = self::xmlToVariable($argument); + } + } + + $class = new ReflectionClass($className); + $variable = $class->newInstanceArgs($constructorArgs); + } else { + $variable = new $className; + } + } + break; + + case 'boolean': { + $variable = $element->nodeValue == 'true' ? TRUE : FALSE; + } + break; + + case 'integer': + case 'double': + case 'string': { + $variable = $element->nodeValue; + + settype($variable, $element->tagName); + } + break; + } + + return $variable; + } + + /** + * Validate list of keys in the associative array. + * + * @param array $hash + * @param array $validKeys + * @return array + * @throws PHPUnit_Framework_Exception + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function assertValidKeys(array $hash, array $validKeys) + { + $valids = array(); + + // Normalize validation keys so that we can use both indexed and + // associative arrays. + foreach ($validKeys as $key => $val) { + is_int($key) ? $valids[$val] = NULL : $valids[$key] = $val; + } + + $validKeys = array_keys($valids); + + // Check for invalid keys. + foreach ($hash as $key => $value) { + if (!in_array($key, $validKeys)) { + $unknown[] = $key; + } + } + + if (!empty($unknown)) { + throw new PHPUnit_Framework_Exception( + 'Unknown key(s): ' . implode(', ', $unknown) + ); + } + + // Add default values for any valid keys that are empty. + foreach ($valids as $key => $value) { + if (!isset($hash[$key])) { + $hash[$key] = $value; + } + } + + return $hash; + } + + /** + * Parse a CSS selector into an associative array suitable for + * use with findNodes(). + * + * @param string $selector + * @param mixed $content + * @return array + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + public static function convertSelectToTag($selector, $content = TRUE) + { + $selector = trim(preg_replace("/\s+/", " ", $selector)); + + // substitute spaces within attribute value + while (preg_match('/\[[^\]]+"[^"]+\s[^"]+"\]/', $selector)) { + $selector = preg_replace( + '/(\[[^\]]+"[^"]+)\s([^"]+"\])/', "$1__SPACE__$2", $selector + ); + } + + if (strstr($selector, ' ')) { + $elements = explode(' ', $selector); + } else { + $elements = array($selector); + } + + $previousTag = array(); + + foreach (array_reverse($elements) as $element) { + $element = str_replace('__SPACE__', ' ', $element); + + // child selector + if ($element == '>') { + $previousTag = array('child' => $previousTag['descendant']); + continue; + } + + $tag = array(); + + // match element tag + preg_match("/^([^\.#\[]*)/", $element, $eltMatches); + + if (!empty($eltMatches[1])) { + $tag['tag'] = $eltMatches[1]; + } + + // match attributes (\[[^\]]*\]*), ids (#[^\.#\[]*), + // and classes (\.[^\.#\[]*)) + preg_match_all( + "/(\[[^\]]*\]*|#[^\.#\[]*|\.[^\.#\[]*)/", $element, $matches + ); + + if (!empty($matches[1])) { + $classes = array(); + $attrs = array(); + + foreach ($matches[1] as $match) { + // id matched + if (substr($match, 0, 1) == '#') { + $tag['id'] = substr($match, 1); + } + + // class matched + else if (substr($match, 0, 1) == '.') { + $classes[] = substr($match, 1); + } + + // attribute matched + else if (substr($match, 0, 1) == '[' && + substr($match, -1, 1) == ']') { + $attribute = substr($match, 1, strlen($match) - 2); + $attribute = str_replace('"', '', $attribute); + + // match single word + if (strstr($attribute, '~=')) { + list($key, $value) = explode('~=', $attribute); + $value = "regexp:/.*\b$value\b.*/"; + } + + // match substring + else if (strstr($attribute, '*=')) { + list($key, $value) = explode('*=', $attribute); + $value = "regexp:/.*$value.*/"; + } + + // exact match + else { + list($key, $value) = explode('=', $attribute); + } + + $attrs[$key] = $value; + } + } + + if ($classes) { + $tag['class'] = join(' ', $classes); + } + + if ($attrs) { + $tag['attributes'] = $attrs; + } + } + + // tag content + if (is_string($content)) { + $tag['content'] = $content; + } + + // determine previous child/descendants + if (!empty($previousTag['descendant'])) { + $tag['descendant'] = $previousTag['descendant']; + } + + else if (!empty($previousTag['child'])) { + $tag['child'] = $previousTag['child']; + } + + $previousTag = array('descendant' => $tag); + } + + return $tag; + } + + /** + * Parse an $actual document and return an array of DOMNodes + * matching the CSS $selector. If an error occurs, it will + * return FALSE. + * + * To only return nodes containing a certain content, give + * the $content to match as a string. Otherwise, setting + * $content to TRUE will return all nodes matching $selector. + * + * The $actual document may be a DOMDocument or a string + * containing XML or HTML, identified by $isHtml. + * + * @param array $selector + * @param string $content + * @param mixed $actual + * @param boolean $isHtml + * @return false|array + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + * @author Tobias Schlitt + */ + public static function cssSelect($selector, $content, $actual, $isHtml = TRUE) + { + $matcher = self::convertSelectToTag($selector, $content); + $dom = self::load($actual, $isHtml); + $tags = self::findNodes($dom, $matcher, $isHtml); + + return $tags; + } + + /** + * Parse out the options from the tag using DOM object tree. + * + * @param DOMDocument $dom + * @param array $options + * @param boolean $isHtml + * @return array + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + * @author Tobias Schlitt + */ + public static function findNodes(DOMDocument $dom, array $options, $isHtml = TRUE) + { + $valid = array( + 'id', 'class', 'tag', 'content', 'attributes', 'parent', + 'child', 'ancestor', 'descendant', 'children' + ); + + $filtered = array(); + $options = self::assertValidKeys($options, $valid); + + // find the element by id + if ($options['id']) { + $options['attributes']['id'] = $options['id']; + } + + if ($options['class']) { + $options['attributes']['class'] = $options['class']; + } + + // find the element by a tag type + if ($options['tag']) { + if ($isHtml) { + $elements = self::getElementsByCaseInsensitiveTagName( + $dom, $options['tag'] + ); + } else { + $elements = $dom->getElementsByTagName($options['tag']); + } + + foreach ($elements as $element) { + $nodes[] = $element; + } + + if (empty($nodes)) { + return FALSE; + } + } + + // no tag selected, get them all + else { + $tags = array( + 'a', 'abbr', 'acronym', 'address', 'area', 'b', 'base', 'bdo', + 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'cite', + 'code', 'col', 'colgroup', 'dd', 'del', 'div', 'dfn', 'dl', + 'dt', 'em', 'fieldset', 'form', 'frame', 'frameset', 'h1', 'h2', + 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'i', 'iframe', + 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', + 'map', 'meta', 'noframes', 'noscript', 'object', 'ol', 'optgroup', + 'option', 'p', 'param', 'pre', 'q', 'samp', 'script', 'select', + 'small', 'span', 'strong', 'style', 'sub', 'sup', 'table', + 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title', + 'tr', 'tt', 'ul', 'var' + ); + + foreach ($tags as $tag) { + if ($isHtml) { + $elements = self::getElementsByCaseInsensitiveTagName( + $dom, $tag + ); + } else { + $elements = $dom->getElementsByTagName($tag); + } + + foreach ($elements as $element) { + $nodes[] = $element; + } + } + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by attributes + if ($options['attributes']) { + foreach ($nodes as $node) { + $invalid = FALSE; + + foreach ($options['attributes'] as $name => $value) { + // match by regexp if like "regexp:/foo/i" + if (preg_match('/^regexp\s*:\s*(.*)/i', $value, $matches)) { + if (!preg_match($matches[1], $node->getAttribute($name))) { + $invalid = TRUE; + } + } + + // class can match only a part + else if ($name == 'class') { + // split to individual classes + $findClasses = explode( + ' ', preg_replace("/\s+/", " ", $value) + ); + + $allClasses = explode( + ' ', + preg_replace("/\s+/", " ", $node->getAttribute($name)) + ); + + // make sure each class given is in the actual node + foreach ($findClasses as $findClass) { + if (!in_array($findClass, $allClasses)) { + $invalid = TRUE; + } + } + } + + // match by exact string + else { + if ($node->getAttribute($name) != $value) { + $invalid = TRUE; + } + } + } + + // if every attribute given matched + if (!$invalid) { + $filtered[] = $node; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by content + if ($options['content'] !== NULL) { + foreach ($nodes as $node) { + $invalid = FALSE; + + // match by regexp if like "regexp:/foo/i" + if (preg_match('/^regexp\s*:\s*(.*)/i', $options['content'], $matches)) { + if (!preg_match($matches[1], self::getNodeText($node))) { + $invalid = TRUE; + } + } + + // match empty string + else if ($options['content'] === '') { + if (self::getNodeText($node) !== '') { + $invalid = TRUE; + } + } + + // match by exact string + else if (strstr(self::getNodeText($node), $options['content']) === FALSE) { + $invalid = TRUE; + } + + if (!$invalid) { + $filtered[] = $node; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by parent node + if ($options['parent']) { + $parentNodes = self::findNodes($dom, $options['parent'], $isHtml); + $parentNode = isset($parentNodes[0]) ? $parentNodes[0] : NULL; + + foreach ($nodes as $node) { + if ($parentNode !== $node->parentNode) { + continue; + } + + $filtered[] = $node; + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by child node + if ($options['child']) { + $childNodes = self::findNodes($dom, $options['child'], $isHtml); + $childNodes = !empty($childNodes) ? $childNodes : array(); + + foreach ($nodes as $node) { + foreach ($node->childNodes as $child) { + foreach ($childNodes as $childNode) { + if ($childNode === $child) { + $filtered[] = $node; + } + } + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by ancestor + if ($options['ancestor']) { + $ancestorNodes = self::findNodes($dom, $options['ancestor'], $isHtml); + $ancestorNode = isset($ancestorNodes[0]) ? $ancestorNodes[0] : NULL; + + foreach ($nodes as $node) { + $parent = $node->parentNode; + + while ($parent && $parent->nodeType != XML_HTML_DOCUMENT_NODE) { + if ($parent === $ancestorNode) { + $filtered[] = $node; + } + + $parent = $parent->parentNode; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by descendant + if ($options['descendant']) { + $descendantNodes = self::findNodes($dom, $options['descendant'], $isHtml); + $descendantNodes = !empty($descendantNodes) ? $descendantNodes : array(); + + foreach ($nodes as $node) { + foreach (self::getDescendants($node) as $descendant) { + foreach ($descendantNodes as $descendantNode) { + if ($descendantNode === $descendant) { + $filtered[] = $node; + } + } + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return FALSE; + } + } + + // filter by children + if ($options['children']) { + $validChild = array('count', 'greater_than', 'less_than', 'only'); + $childOptions = self::assertValidKeys( + $options['children'], $validChild + ); + + foreach ($nodes as $node) { + $childNodes = $node->childNodes; + + foreach ($childNodes as $childNode) { + if ($childNode->nodeType !== XML_CDATA_SECTION_NODE && + $childNode->nodeType !== XML_TEXT_NODE) { + $children[] = $childNode; + } + } + + // we must have children to pass this filter + if (!empty($children)) { + // exact count of children + if ($childOptions['count'] !== NULL) { + if (count($children) !== $childOptions['count']) { + break; + } + } + + // range count of children + else if ($childOptions['less_than'] !== NULL && + $childOptions['greater_than'] !== NULL) { + if (count($children) >= $childOptions['less_than'] || + count($children) <= $childOptions['greater_than']) { + break; + } + } + + // less than a given count + else if ($childOptions['less_than'] !== NULL) { + if (count($children) >= $childOptions['less_than']) { + break; + } + } + + // more than a given count + else if ($childOptions['greater_than'] !== NULL) { + if (count($children) <= $childOptions['greater_than']) { + break; + } + } + + // match each child against a specific tag + if ($childOptions['only']) { + $onlyNodes = self::findNodes( + $dom, $childOptions['only'], $isHtml + ); + + // try to match each child to one of the 'only' nodes + foreach ($children as $child) { + $matched = FALSE; + + foreach ($onlyNodes as $onlyNode) { + if ($onlyNode === $child) { + $matched = TRUE; + } + } + + if (!$matched) { + break(2); + } + } + } + + $filtered[] = $node; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return; + } + } + + // return the first node that matches all criteria + return !empty($nodes) ? $nodes : array(); + } + + /** + * Recursively get flat array of all descendants of this node. + * + * @param DOMNode $node + * @return array + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + protected static function getDescendants(DOMNode $node) + { + $allChildren = array(); + $childNodes = $node->childNodes ? $node->childNodes : array(); + + foreach ($childNodes as $child) { + if ($child->nodeType === XML_CDATA_SECTION_NODE || + $child->nodeType === XML_TEXT_NODE) { + continue; + } + + $children = self::getDescendants($child); + $allChildren = array_merge($allChildren, $children, array($child)); + } + + return isset($allChildren) ? $allChildren : array(); + } + + /** + * Gets elements by case insensitive tagname. + * + * @param DOMDocument $dom + * @param string $tag + * @return DOMNodeList + * @since Method available since Release 3.4.0 + */ + protected static function getElementsByCaseInsensitiveTagName(DOMDocument $dom, $tag) + { + $elements = $dom->getElementsByTagName(strtolower($tag)); + + if ($elements->length == 0) { + $elements = $dom->getElementsByTagName(strtoupper($tag)); + } + + return $elements; + } + + /** + * Get the text value of this node's child text node. + * + * @param DOMNode $node + * @return string + * @since Method available since Release 3.3.0 + * @author Mike Naberezny + * @author Derek DeVries + */ + protected static function getNodeText(DOMNode $node) + { + if (!$node->childNodes instanceof DOMNodeList) { + return ''; + } + + $result = ''; + + foreach ($node->childNodes as $childNode) { + if ($childNode->nodeType === XML_TEXT_NODE) { + $result .= trim($childNode->data) . ' '; + } else { + $result .= self::getNodeText($childNode); + } + } + + return str_replace(' ', ' ', $result); + } +} diff --git a/vendor/phpunit/phpunit/README.md b/vendor/phpunit/phpunit/README.md new file mode 100644 index 0000000..397be60 --- /dev/null +++ b/vendor/phpunit/phpunit/README.md @@ -0,0 +1,71 @@ +# PHPUnit + +PHPUnit is the de-facto standard for unit testing in PHP projects. It provides both a framework that makes the writing of tests easy as well as the functionality to easily run the tests and analyse their results. + +## Requirements + +* PHPUnit 3.7 requires PHP 5.3.3 (or later) but PHP 5.4.6 (or later) is highly recommended. +* [PHP_CodeCoverage](http://github.com/sebastianbergmann/php-code-coverage), the library that is used by PHPUnit to collect and process code coverage information, depends on [Xdebug](http://xdebug.org/) 2.0.5 (or later) but Xdebug 2.2.1 (or later) is highly recommended. + +## Installation + +There a three supported ways of installing PHPUnit. + +You can use the [PEAR Installer](http://pear.php.net/manual/en/guide.users.commandline.cli.php) or [Composer](http://getcomposer.org/) to download and install PHPUnit as well as its dependencies. You can also download a [PHP Archive (PHAR)](http://php.net/phar) of PHPUnit that has all required (as well as some optional) dependencies of PHPUnit bundled in a single file. + +### PEAR Installer + +The following two commands (which you may have to run as `root`) are all that is required to install PHPUnit using the PEAR Installer: + + pear config-set auto_discover 1 + pear install pear.phpunit.de/PHPUnit + +### Composer + +To add PHPUnit as a local, per-project dependency to your project, simply add a dependency on `phpunit/phpunit` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a development-time dependency on PHPUnit 3.7: + + { + "require-dev": { + "phpunit/phpunit": "3.7.*" + } + } + +### PHP Archive (PHAR) + + wget http://pear.phpunit.de/get/phpunit.phar + chmod +x phpunit.phar + +## Documentation + +The documentation for PHPUnit is available in different formats: + +* [English, multiple HTML files](http://www.phpunit.de/manual/3.7/en/index.html) +* [English, single HTML file](http://www.phpunit.de/manual/3.7/en/phpunit-book.html) +* [English, PDF](http://www.phpunit.de/manual/3.7/en/phpunit-book.pdf) +* [English, ePub](http://www.phpunit.de/manual/3.7/en/phpunit-book.epub) +* [French, multiple HTML files](http://www.phpunit.de/manual/3.7/fr/index.html) +* [French, single HTML file](http://www.phpunit.de/manual/3.7/fr/phpunit-book.html) +* [French, PDF](http://www.phpunit.de/manual/3.7/fr/phpunit-book.pdf) +* [French, ePub](http://www.phpunit.de/manual/3.7/fr/phpunit-book.epub) +* [Japanese, multiple HTML files](http://www.phpunit.de/manual/3.7/ja/index.html) +* [Japanese, single HTML file](http://www.phpunit.de/manual/3.7/ja/phpunit-book.html) +* [Japanese, PDF](http://www.phpunit.de/manual/3.7/ja/phpunit-book.pdf) +* [Japanese, ePub](http://www.phpunit.de/manual/3.7/ja/phpunit-book.epub) + +## IRC + +The [#phpunit channel on the Freenode IRC network](irc://irc.freenode.net/phpunit) is a place to chat about PHPUnit. + +## List of Contributors + +Thanks to everyone who has contributed to PHPUnit! You can find a detailed list of contributors on every PHPUnit related package on GitHub. This list shows only the major components: + +* [PHPUnit](https://github.com/sebastianbergmann/phpunit/graphs/contributors) +* [PHP_CodeCoverage](https://github.com/sebastianbergmann/php-code-coverage/graphs/contributors) +* [PHPUnit_MockObject](https://github.com/sebastianbergmann/phpunit-mock-objects/graphs/contributors) + +A very special thanks to everyone who has contributed to the documentation and helps maintaining the translations: + +* [PHPUnit Documentation](https://github.com/sebastianbergmann/phpunit-documentation/graphs/contributors) + +Please refer to [CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md) for information on how to contribute to PHPUnit and its related projects. diff --git a/vendor/phpunit/phpunit/Tests/Extensions/RepeatedTestTest.php b/vendor/phpunit/phpunit/Tests/Extensions/RepeatedTestTest.php new file mode 100644 index 0000000..7e8935f --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Extensions/RepeatedTestTest.php @@ -0,0 +1,108 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Success.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Extensions_RepeatedTestTest extends PHPUnit_Framework_TestCase +{ + protected $suite; + + public function __construct() + { + $this->suite = new PHPUnit_Framework_TestSuite; + + $this->suite->addTest(new Success); + $this->suite->addTest(new Success); + } + + public function testRepeatedOnce() + { + $test = new PHPUnit_Extensions_RepeatedTest($this->suite, 1); + $this->assertEquals(2, count($test)); + + $result = $test->run(); + $this->assertEquals(2, count($result)); + } + + public function testRepeatedMoreThanOnce() + { + $test = new PHPUnit_Extensions_RepeatedTest($this->suite, 3); + $this->assertEquals(6, count($test)); + + $result = $test->run(); + $this->assertEquals(6, count($result)); + } + + public function testRepeatedZero() + { + $test = new PHPUnit_Extensions_RepeatedTest($this->suite, 0); + $this->assertEquals(0, count($test)); + + $result = $test->run(); + $this->assertEquals(0, count($result)); + } + + public function testRepeatedNegative() + { + try { + $test = new PHPUnit_Extensions_RepeatedTest($this->suite, -1); + } + + catch (Exception $e) { + return; + } + + $this->fail('Should throw an Exception'); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/AssertTest.php b/vendor/phpunit/phpunit/Tests/Framework/AssertTest.php new file mode 100644 index 0000000..547c45e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/AssertTest.php @@ -0,0 +1,4158 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ClassWithNonPublicAttributes.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'SampleClass.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Struct.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'TestIterator.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Author.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Book.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ClassWithToString.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Framework_AssertTest extends PHPUnit_Framework_TestCase +{ + protected $filesDirectory; + + protected function setUp() + { + $this->filesDirectory = dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR; + + $this->html = file_get_contents( + $this->filesDirectory . 'SelectorAssertionsFixture.html' + ); + } + + /** + * @covers PHPUnit_Framework_Assert::fail + */ + public function testFail() + { + try { + $this->fail(); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + throw new PHPUnit_Framework_AssertionFailedError('Fail did not throw fail exception'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + */ + public function testAssertSplObjectStorageContainsObject() + { + $a = new stdClass; + $b = new stdClass; + $c = new SplObjectStorage; + $c->attach($a); + + $this->assertContains($a, $c); + + try { + $this->assertContains($b, $c); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + */ + public function testAssertArrayContainsObject() + { + $a = new stdClass; + $b = new stdClass; + + $this->assertContains($a, array($a)); + + try { + $this->assertContains($a, array($b)); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + */ + public function testAssertArrayContainsString() + { + $this->assertContains('foo', array('foo')); + + try { + $this->assertContains('foo', array('bar')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + /** + * @covers PHPUnit_Framework_Assert::assertContainsOnlyInstancesOf + */ + public function testAssertContainsOnlyInstancesOf() + { + $test = array( + new Book(), + new Book + ); + $this->assertContainsOnlyInstancesOf('Book', $test); + $this->assertContainsOnlyInstancesOf('stdClass', array(new stdClass())); + + $test2 = array( + new Author('Test') + ); + try { + $this->assertContainsOnlyInstancesOf('Book', $test2); + } catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayHasKey + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertArrayHasKeyThrowsException() + { + $this->assertArrayHasKey(NULL, array()); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayHasKey + */ + public function testAssertArrayHasIntegerKey() + { + $this->assertArrayHasKey(0, array('foo')); + + try { + $this->assertArrayHasKey(1, array('foo')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayNotHasKey + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertArrayNotHasKeyThrowsException() + { + $this->assertArrayNotHasKey(NULL, array()); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayNotHasKey + */ + public function testAssertArrayNotHasIntegerKey() + { + $this->assertArrayNotHasKey(1, array('foo')); + + try { + $this->assertArrayNotHasKey(0, array('foo')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayHasKey + */ + public function testAssertArrayHasStringKey() + { + $this->assertArrayHasKey('foo', array('foo' => 'bar')); + + try { + $this->assertArrayHasKey('bar', array('foo' => 'bar')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayNotHasKey + */ + public function testAssertArrayNotHasStringKey() + { + $this->assertArrayNotHasKey('bar', array('foo' => 'bar')); + + try { + $this->assertArrayNotHasKey('foo', array('foo' => 'bar')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayHasKey + */ + public function testAssertArrayHasKeyAcceptsArrayAccessValue() + { + $array = new ArrayObject(); + $array['foo'] = 'bar'; + $this->assertArrayHasKey('foo', $array); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayHasKey + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertArrayHasKeyProperlyFailsWithArrayAccessValue() + { + $array = new ArrayObject(); + $array['bar'] = 'bar'; + $this->assertArrayHasKey('foo', $array); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayNotHasKey + */ + public function testAssertArrayNotHasKeyAcceptsArrayAccessValue() + { + $array = new ArrayObject(); + $array['foo'] = 'bar'; + $this->assertArrayNotHasKey('bar', $array); + } + + /** + * @covers PHPUnit_Framework_Assert::assertArrayNotHasKey + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertArrayNotHasKeyPropertlyFailsWithArrayAccessValue() + { + $array = new ArrayObject(); + $array['bar'] = 'bar'; + $this->assertArrayNotHasKey('bar', $array); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertContainsThrowsException() + { + $this->assertContains(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + */ + public function testAssertIteratorContainsObject() + { + $foo = new stdClass; + + $this->assertContains($foo, new TestIterator(array($foo))); + + try { + $this->assertContains($foo, new TestIterator(array(new stdClass))); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + */ + public function testAssertIteratorContainsString() + { + $this->assertContains('foo', new TestIterator(array('foo'))); + + try { + $this->assertContains('foo', new TestIterator(array('bar'))); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContains + */ + public function testAssertStringContainsString() + { + $this->assertContains('foo', 'foobar'); + + try { + $this->assertContains('foo', 'bar'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContains + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertNotContainsThrowsException() + { + $this->assertNotContains(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContains + */ + public function testAssertSplObjectStorageNotContainsObject() + { + $a = new stdClass; + $b = new stdClass; + $c = new SplObjectStorage; + $c->attach($a); + + $this->assertNotContains($b, $c); + + try { + $this->assertNotContains($a, $c); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContains + */ + public function testAssertArrayNotContainsObject() + { + $a = new stdClass; + $b = new stdClass; + + $this->assertNotContains($a, array($b)); + + try { + $this->assertNotContains($a, array($a)); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContains + */ + public function testAssertArrayNotContainsString() + { + $this->assertNotContains('foo', array('bar')); + + try { + $this->assertNotContains('foo', array('foo')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContains + */ + public function testAssertStringNotContainsString() + { + $this->assertNotContains('foo', 'bar'); + + try { + $this->assertNotContains('foo', 'foo'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContainsOnly + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertContainsOnlyThrowsException() + { + $this->assertContainsOnly(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContainsOnlyInstancesOf + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertContainsOnlyInstancesOfThrowsException() + { + $this->assertContainsOnlyInstancesOf(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContainsOnly + */ + public function testAssertArrayContainsOnlyIntegers() + { + $this->assertContainsOnly('integer', array(1, 2, 3)); + + try { + $this->assertContainsOnly('integer', array("1", 2, 3)); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContainsOnly + */ + public function testAssertArrayNotContainsOnlyIntegers() + { + $this->assertNotContainsOnly('integer', array("1", 2, 3)); + + try { + $this->assertNotContainsOnly('integer', array(1, 2, 3)); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertContainsOnly + */ + public function testAssertArrayContainsOnlyStdClass() + { + $this->assertContainsOnly('StdClass', array(new StdClass)); + + try { + $this->assertContainsOnly('StdClass', array('StdClass')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotContainsOnly + */ + public function testAssertArrayNotContainsOnlyStdClass() + { + $this->assertNotContainsOnly('StdClass', array('StdClass')); + + try { + $this->assertNotContainsOnly('StdClass', array(new StdClass)); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + protected function createDOMDocument($content) + { + $document = new DOMDocument; + $document->preserveWhiteSpace = FALSE; + $document->loadXML($content); + + return $document; + } + + protected function sameValues() + { + $object = new SampleClass(4, 8, 15); + // cannot use $filesDirectory, because neither setUp() nor + // setUpBeforeClass() are executed before the data providers + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'foo.xml'; + $resource = fopen($file, 'r'); + + return array( + // NULL + array(NULL, NULL), + // strings + array('a', 'a'), + // integers + array(0, 0), + // floats + array(2.3, 2.3), + array(1/3, 1 - 2/3), + array(log(0), log(0)), + // arrays + array(array(), array()), + array(array(0 => 1), array(0 => 1)), + array(array(0 => NULL), array(0 => NULL)), + array(array('a', 'b' => array(1, 2)), array('a', 'b' => array(1, 2))), + // objects + array($object, $object), + // resources + array($resource, $resource), + ); + } + + protected function notEqualValues() + { + // cyclic dependencies + $book1 = new Book; + $book1->author = new Author('Terry Pratchett'); + $book1->author->books[] = $book1; + $book2 = new Book; + $book2->author = new Author('Terry Pratch'); + $book2->author->books[] = $book2; + + $book3 = new Book; + $book3->author = 'Terry Pratchett'; + $book4 = new stdClass; + $book4->author = 'Terry Pratchett'; + + $object1 = new SampleClass( 4, 8, 15); + $object2 = new SampleClass(16, 23, 42); + $object3 = new SampleClass( 4, 8, 15); + $storage1 = new SplObjectStorage; + $storage1->attach($object1); + $storage2 = new SplObjectStorage; + $storage2->attach($object3); // same content, different object + + // cannot use $filesDirectory, because neither setUp() nor + // setUpBeforeClass() are executed before the data providers + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'foo.xml'; + + return array( + // strings + array('a', 'b'), + array('a', 'A'), + // integers + array(1, 2), + array(2, 1), + // floats + array(2.3, 4.2), + array(2.3, 4.2, 0.5), + array(array(2.3), array(4.2), 0.5), + array(array(array(2.3)), array(array(4.2)), 0.5), + array(new Struct(2.3), new Struct(4.2), 0.5), + array(array(new Struct(2.3)), array(new Struct(4.2)), 0.5), + // arrays + array(array(), array(0 => 1)), + array(array(0 => 1), array()), + array(array(0 => NULL), array()), + array(array(0 => 1, 1 => 2), array(0 => 1, 1 => 3)), + array(array('a', 'b' => array(1, 2)), array('a', 'b' => array(2, 1))), + // objects + array(new SampleClass(4, 8, 15), new SampleClass(16, 23, 42)), + array($object1, $object2), + array($book1, $book2), + array($book3, $book4), // same content, different class + // resources + array(fopen($file, 'r'), fopen($file, 'r')), + // SplObjectStorage + array($storage1, $storage2), + // DOMDocument + array( + $this->createDOMDocument(''), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument(' bar '), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument(' bar '), + $this->createDOMDocument(' bir '), + ), + // Exception + //array(new Exception('Exception 1'), new Exception('Exception 2')), + // different types + array(new SampleClass(4, 8, 15), FALSE), + array(FALSE, new SampleClass(4, 8, 15)), + array(array(0 => 1, 1 => 2), FALSE), + array(FALSE, array(0 => 1, 1 => 2)), + array(array(), new stdClass), + array(new stdClass, array()), + // PHP: 0 == 'Foobar' => TRUE! + // We want these values to differ + array(0, 'Foobar'), + array('Foobar', 0), + array(3, acos(8)), + array(acos(8), 3) + ); + } + + protected function equalValues() + { + // cyclic dependencies + $book1 = new Book; + $book1->author = new Author('Terry Pratchett'); + $book1->author->books[] = $book1; + $book2 = new Book; + $book2->author = new Author('Terry Pratchett'); + $book2->author->books[] = $book2; + + $object1 = new SampleClass(4, 8, 15); + $object2 = new SampleClass(4, 8, 15); + $storage1 = new SplObjectStorage; + $storage1->attach($object1); + $storage2 = new SplObjectStorage; + $storage2->attach($object1); + + return array( + // strings + array('a', 'A', 0, FALSE, TRUE), // ignore case + // arrays + array(array('a' => 1, 'b' => 2), array('b' => 2, 'a' => 1)), + array(array(1), array('1')), + array(array(3, 2, 1), array(2, 3, 1), 0, TRUE), // canonicalized comparison + // floats + array(2.3, 2.5, 0.5), + array(array(2.3), array(2.5), 0.5), + array(array(array(2.3)), array(array(2.5)), 0.5), + array(new Struct(2.3), new Struct(2.5), 0.5), + array(array(new Struct(2.3)), array(new Struct(2.5)), 0.5), + // numeric with delta + array(1, 2, 1), + // objects + array($object1, $object2), + array($book1, $book2), + // SplObjectStorage + array($storage1, $storage2), + // DOMDocument + array( + $this->createDOMDocument(''), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument(''), + ), + array( + $this->createDOMDocument("\n \n"), + $this->createDOMDocument(''), + ), + // Exception + //array(new Exception('Exception 1'), new Exception('Exception 1')), + // mixed types + array(0, '0'), + array('0', 0), + array(2.3, '2.3'), + array('2.3', 2.3), + array((string)(1/3), 1 - 2/3), + array(1/3, (string)(1 - 2/3)), + array('string representation', new ClassWithToString), + array(new ClassWithToString, 'string representation'), + ); + } + + public function equalProvider() + { + // same |= equal + return array_merge($this->equalValues(), $this->sameValues()); + } + + public function notEqualProvider() + { + return $this->notEqualValues(); + } + + public function sameProvider() + { + return $this->sameValues(); + } + + public function notSameProvider() + { + // not equal |= not same + // equal, ¬same |= not same + return array_merge($this->notEqualValues(), $this->equalValues()); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEquals + * @dataProvider equalProvider + */ + public function testAssertEqualsSucceeds($a, $b, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + $this->assertEquals($a, $b, '', $delta, 10, $canonicalize, $ignoreCase); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEquals + * @dataProvider notEqualProvider + */ + public function testAssertEqualsFails($a, $b, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + try { + $this->assertEquals($a, $b, '', $delta, 10, $canonicalize, $ignoreCase); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotEquals + * @dataProvider notEqualProvider + */ + public function testAssertNotEqualsSucceeds($a, $b, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + $this->assertNotEquals($a, $b, '', $delta, 10, $canonicalize, $ignoreCase); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotEquals + * @dataProvider equalProvider + */ + public function testAssertNotEqualsFails($a, $b, $delta = 0, $canonicalize = FALSE, $ignoreCase = FALSE) + { + try { + $this->assertNotEquals($a, $b, '', $delta, 10, $canonicalize, $ignoreCase); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSame + * @dataProvider sameProvider + */ + public function testAssertSameSucceeds($a, $b) + { + $this->assertSame($a, $b); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSame + * @dataProvider notSameProvider + */ + public function testAssertSameFails($a, $b) + { + try { + $this->assertSame($a, $b); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotSame + * @dataProvider notSameProvider + */ + public function testAssertNotSameSucceeds($a, $b) + { + $this->assertNotSame($a, $b); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotSame + * @dataProvider sameProvider + */ + public function testAssertNotSameFails($a, $b) + { + try { + $this->assertNotSame($a, $b); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertXmlFileEqualsXmlFile + */ + public function testAssertXmlFileEqualsXmlFile() + { + $this->assertXmlFileEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'foo.xml' + ); + + try { + $this->assertXmlFileEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'bar.xml' + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertXmlFileNotEqualsXmlFile + */ + public function testAssertXmlFileNotEqualsXmlFile() + { + $this->assertXmlFileNotEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'bar.xml' + ); + + try { + $this->assertXmlFileNotEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'foo.xml' + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertXmlStringEqualsXmlFile + */ + public function testAssertXmlStringEqualsXmlFile() + { + $this->assertXmlStringEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'foo.xml') + ); + + try { + $this->assertXmlStringEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'bar.xml') + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlFile + */ + public function testXmlStringNotEqualsXmlFile() + { + $this->assertXmlStringNotEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'bar.xml') + ); + + try { + $this->assertXmlStringNotEqualsXmlFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'foo.xml') + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertXmlStringEqualsXmlString + */ + public function testAssertXmlStringEqualsXmlString() + { + $this->assertXmlStringEqualsXmlString('', ''); + + try { + $this->assertXmlStringEqualsXmlString('', ''); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlString + */ + public function testAssertXmlStringNotEqualsXmlString() + { + $this->assertXmlStringNotEqualsXmlString('', ''); + + try { + $this->assertXmlStringNotEqualsXmlString('', ''); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEqualXMLStructure + */ + public function testXMLStructureIsSame() + { + $expected = new DOMDocument; + $expected->load($this->filesDirectory . 'structureExpected.xml'); + + $actual = new DOMDocument; + $actual->load($this->filesDirectory . 'structureExpected.xml'); + + $this->assertEqualXMLStructure( + $expected->firstChild, $actual->firstChild, TRUE + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEqualXMLStructure + * @expectedException PHPUnit_Framework_ExpectationFailedException + */ + public function testXMLStructureWrongNumberOfAttributes() + { + $expected = new DOMDocument; + $expected->load($this->filesDirectory . 'structureExpected.xml'); + + $actual = new DOMDocument; + $actual->load($this->filesDirectory . 'structureWrongNumberOfAttributes.xml'); + + $this->assertEqualXMLStructure( + $expected->firstChild, $actual->firstChild, TRUE + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEqualXMLStructure + * @expectedException PHPUnit_Framework_ExpectationFailedException + */ + public function testXMLStructureWrongNumberOfNodes() + { + $expected = new DOMDocument; + $expected->load($this->filesDirectory . 'structureExpected.xml'); + + $actual = new DOMDocument; + $actual->load($this->filesDirectory . 'structureWrongNumberOfNodes.xml'); + + $this->assertEqualXMLStructure( + $expected->firstChild, $actual->firstChild, TRUE + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEqualXMLStructure + */ + public function testXMLStructureIsSameButDataIsNot() + { + $expected = new DOMDocument; + $expected->load($this->filesDirectory . 'structureExpected.xml'); + + $actual = new DOMDocument; + $actual->load($this->filesDirectory . 'structureIsSameButDataIsNot.xml'); + + $this->assertEqualXMLStructure( + $expected->firstChild, $actual->firstChild, TRUE + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEqualXMLStructure + */ + public function testXMLStructureAttributesAreSameButValuesAreNot() + { + $expected = new DOMDocument; + $expected->load($this->filesDirectory . 'structureExpected.xml'); + + $actual = new DOMDocument; + $actual->load($this->filesDirectory . 'structureAttributesAreSameButValuesAreNot.xml'); + + $this->assertEqualXMLStructure( + $expected->firstChild, $actual->firstChild, TRUE + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEqualXMLStructure + */ + public function testXMLStructureIgnoreTextNodes() + { + $expected = new DOMDocument; + $expected->load($this->filesDirectory . 'structureExpected.xml'); + + $actual = new DOMDocument; + $actual->load($this->filesDirectory . 'structureIgnoreTextNodes.xml'); + + $this->assertEqualXMLStructure( + $expected->firstChild, $actual->firstChild, TRUE + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEquals + */ + public function testAssertStringEqualsNumeric() + { + $this->assertEquals('0', 0); + + try { + $this->assertEquals('0', 1); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotEquals + */ + public function testAssertStringEqualsNumeric2() + { + $this->assertNotEquals('A', 0); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFileExists + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertFileExistsThrowsException() + { + $this->assertFileExists(NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFileExists + */ + public function testAssertFileExists() + { + $this->assertFileExists(__FILE__); + + try { + $this->assertFileExists(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFileNotExists + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertFileNotExistsThrowsException() + { + $this->assertFileNotExists(NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFileNotExists + */ + public function testAssertFileNotExists() + { + $this->assertFileNotExists(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); + + try { + $this->assertFileNotExists(__FILE__); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + */ + public function testAssertObjectHasAttribute() + { + $o = new Author('Terry Pratchett'); + + $this->assertObjectHasAttribute('name', $o); + + try { + $this->assertObjectHasAttribute('foo', $o); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + */ + public function testAssertObjectNotHasAttribute() + { + $o = new Author('Terry Pratchett'); + + $this->assertObjectNotHasAttribute('foo', $o); + + try { + $this->assertObjectNotHasAttribute('name', $o); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNull + */ + public function testAssertNull() + { + $this->assertNull(NULL); + + try { + $this->assertNull(new stdClass); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotNull + */ + public function testAssertNotNull() + { + $this->assertNotNull(new stdClass); + + try { + $this->assertNotNull(NULL); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTrue + */ + public function testAssertTrue() + { + $this->assertTrue(TRUE); + + try { + $this->assertTrue(FALSE); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFalse + */ + public function testAssertFalse() + { + $this->assertFalse(FALSE); + + try { + $this->assertFalse(TRUE); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertRegExp + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertRegExpThrowsException() + { + $this->assertRegExp(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertRegExp + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertRegExpThrowsException2() + { + $this->assertRegExp('', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotRegExp + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertNotRegExpThrowsException() + { + $this->assertNotRegExp(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotRegExp + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertNotRegExpThrowsException2() + { + $this->assertNotRegExp('', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertRegExp + */ + public function testAssertRegExp() + { + $this->assertRegExp('/foo/', 'foobar'); + + try { + $this->assertRegExp('/foo/', 'bar'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotRegExp + */ + public function testAssertNotRegExp() + { + $this->assertNotRegExp('/foo/', 'bar'); + + try { + $this->assertNotRegExp('/foo/', 'foobar'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSame + */ + public function testAssertSame() + { + $o = new stdClass; + + $this->assertSame($o, $o); + + try { + $this->assertSame( + new stdClass, + new stdClass + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSame + */ + public function testAssertSame2() + { + $this->assertSame(TRUE, TRUE); + $this->assertSame(FALSE, FALSE); + + try { + $this->assertSame(TRUE, FALSE); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotSame + */ + public function testAssertNotSame() + { + $this->assertNotSame( + new stdClass, + NULL + ); + + $this->assertNotSame( + NULL, + new stdClass + ); + + $this->assertNotSame( + new stdClass, + new stdClass + ); + + $o = new stdClass; + + try { + $this->assertNotSame($o, $o); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotSame + */ + public function testAssertNotSame2() + { + $this->assertNotSame(TRUE, FALSE); + $this->assertNotSame(FALSE, TRUE); + + try { + $this->assertNotSame(TRUE, TRUE); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotSame + */ + public function testAssertNotSameFailsNull() + { + try { + $this->assertNotSame(NULL, NULL); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertGreaterThan + */ + public function testGreaterThan() + { + $this->assertGreaterThan(1, 2); + + try { + $this->assertGreaterThan(2, 1); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeGreaterThan + */ + public function testAttributeGreaterThan() + { + $this->assertAttributeGreaterThan( + 1, 'bar', new ClassWithNonPublicAttributes + ); + + try { + $this->assertAttributeGreaterThan( + 1, 'foo', new ClassWithNonPublicAttributes + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertGreaterThanOrEqual + */ + public function testGreaterThanOrEqual() + { + $this->assertGreaterThanOrEqual(1, 2); + + try { + $this->assertGreaterThanOrEqual(2, 1); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeGreaterThanOrEqual + */ + public function testAttributeGreaterThanOrEqual() + { + $this->assertAttributeGreaterThanOrEqual( + 1, 'bar', new ClassWithNonPublicAttributes + ); + + try { + $this->assertAttributeGreaterThanOrEqual( + 2, 'foo', new ClassWithNonPublicAttributes + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertLessThan + */ + public function testLessThan() + { + $this->assertLessThan(2, 1); + + try { + $this->assertLessThan(1, 2); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeLessThan + */ + public function testAttributeLessThan() + { + $this->assertAttributeLessThan( + 2, 'foo', new ClassWithNonPublicAttributes + ); + + try { + $this->assertAttributeLessThan( + 1, 'bar', new ClassWithNonPublicAttributes + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertLessThanOrEqual + */ + public function testLessThanOrEqual() + { + $this->assertLessThanOrEqual(2, 1); + + try { + $this->assertLessThanOrEqual(1, 2); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeLessThanOrEqual + */ + public function testAttributeLessThanOrEqual() + { + $this->assertAttributeLessThanOrEqual( + 2, 'foo', new ClassWithNonPublicAttributes + ); + + try { + $this->assertAttributeLessThanOrEqual( + 1, 'bar', new ClassWithNonPublicAttributes + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::readAttribute + */ + public function testReadAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertEquals('foo', $this->readAttribute($obj, 'publicAttribute')); + $this->assertEquals('bar', $this->readAttribute($obj, 'protectedAttribute')); + $this->assertEquals('baz', $this->readAttribute($obj, 'privateAttribute')); + $this->assertEquals('bar', $this->readAttribute($obj, 'protectedParentAttribute')); + //$this->assertEquals('bar', $this->readAttribute($obj, 'privateParentAttribute')); + } + + /** + * @covers PHPUnit_Framework_Assert::readAttribute + */ + public function testReadAttribute2() + { + $this->assertEquals('foo', $this->readAttribute('ClassWithNonPublicAttributes', 'publicStaticAttribute')); + $this->assertEquals('bar', $this->readAttribute('ClassWithNonPublicAttributes', 'protectedStaticAttribute')); + $this->assertEquals('baz', $this->readAttribute('ClassWithNonPublicAttributes', 'privateStaticAttribute')); + $this->assertEquals('foo', $this->readAttribute('ClassWithNonPublicAttributes', 'protectedStaticParentAttribute')); + $this->assertEquals('foo', $this->readAttribute('ClassWithNonPublicAttributes', 'privateStaticParentAttribute')); + } + + /** + * @covers PHPUnit_Framework_Assert::readAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testReadAttribute3() + { + $this->readAttribute('StdClass', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::readAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testReadAttribute4() + { + $this->readAttribute('NotExistingClass', 'foo'); + } + + /** + * @covers PHPUnit_Framework_Assert::readAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testReadAttribute5() + { + $this->readAttribute(NULL, 'foo'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeContains + */ + public function testAssertPublicAttributeContains() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeContains('foo', 'publicArray', $obj); + + try { + $this->assertAttributeContains('bar', 'publicArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeContainsOnly + */ + public function testAssertPublicAttributeContainsOnly() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeContainsOnly('string', 'publicArray', $obj); + + try { + $this->assertAttributeContainsOnly('integer', 'publicArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotContains + */ + public function testAssertPublicAttributeNotContains() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotContains('bar', 'publicArray', $obj); + + try { + $this->assertAttributeNotContains('foo', 'publicArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotContainsOnly + */ + public function testAssertPublicAttributeNotContainsOnly() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotContainsOnly('integer', 'publicArray', $obj); + + try { + $this->assertAttributeNotContainsOnly('string', 'publicArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeContains + */ + public function testAssertProtectedAttributeContains() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeContains('bar', 'protectedArray', $obj); + + try { + $this->assertAttributeContains('foo', 'protectedArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotContains + */ + public function testAssertProtectedAttributeNotContains() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotContains('foo', 'protectedArray', $obj); + + try { + $this->assertAttributeNotContains('bar', 'protectedArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeContains + */ + public function testAssertPrivateAttributeContains() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeContains('baz', 'privateArray', $obj); + + try { + $this->assertAttributeContains('foo', 'privateArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotContains + */ + public function testAssertPrivateAttributeNotContains() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotContains('foo', 'privateArray', $obj); + + try { + $this->assertAttributeNotContains('baz', 'privateArray', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEquals + */ + public function testAssertPublicAttributeEquals() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeEquals('foo', 'publicAttribute', $obj); + + try { + $this->assertAttributeEquals('bar', 'publicAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEquals + */ + public function testAssertPublicAttributeNotEquals() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotEquals('bar', 'publicAttribute', $obj); + + try { + $this->assertAttributeNotEquals('foo', 'publicAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeSame + */ + public function testAssertPublicAttributeSame() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeSame('foo', 'publicAttribute', $obj); + + try { + $this->assertAttributeSame('bar', 'publicAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotSame + */ + public function testAssertPublicAttributeNotSame() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotSame('bar', 'publicAttribute', $obj); + + try { + $this->assertAttributeNotSame('foo', 'publicAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEquals + */ + public function testAssertProtectedAttributeEquals() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeEquals('bar', 'protectedAttribute', $obj); + + try { + $this->assertAttributeEquals('foo', 'protectedAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEquals + */ + public function testAssertProtectedAttributeNotEquals() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotEquals('foo', 'protectedAttribute', $obj); + + try { + $this->assertAttributeNotEquals('bar', 'protectedAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEquals + */ + public function testAssertPrivateAttributeEquals() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeEquals('baz', 'privateAttribute', $obj); + + try { + $this->assertAttributeEquals('foo', 'privateAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEquals + */ + public function testAssertPrivateAttributeNotEquals() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertAttributeNotEquals('foo', 'privateAttribute', $obj); + + try { + $this->assertAttributeNotEquals('baz', 'privateAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEquals + */ + public function testAssertPublicStaticAttributeEquals() + { + $this->assertAttributeEquals('foo', 'publicStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertAttributeEquals('bar', 'publicStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEquals + */ + public function testAssertPublicStaticAttributeNotEquals() + { + $this->assertAttributeNotEquals('bar', 'publicStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertAttributeNotEquals('foo', 'publicStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEquals + */ + public function testAssertProtectedStaticAttributeEquals() + { + $this->assertAttributeEquals('bar', 'protectedStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertAttributeEquals('foo', 'protectedStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEquals + */ + public function testAssertProtectedStaticAttributeNotEquals() + { + $this->assertAttributeNotEquals('foo', 'protectedStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertAttributeNotEquals('bar', 'protectedStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEquals + */ + public function testAssertPrivateStaticAttributeEquals() + { + $this->assertAttributeEquals('baz', 'privateStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertAttributeEquals('foo', 'privateStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEquals + */ + public function testAssertPrivateStaticAttributeNotEquals() + { + $this->assertAttributeNotEquals('foo', 'privateStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertAttributeNotEquals('baz', 'privateStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassHasAttributeThrowsException() + { + $this->assertClassHasAttribute(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassHasAttributeThrowsException2() + { + $this->assertClassHasAttribute('foo', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassNotHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassNotHasAttributeThrowsException() + { + $this->assertClassNotHasAttribute(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassNotHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassNotHasAttributeThrowsException2() + { + $this->assertClassNotHasAttribute('foo', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassHasStaticAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassHasStaticAttributeThrowsException() + { + $this->assertClassHasStaticAttribute(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassHasStaticAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassHasStaticAttributeThrowsException2() + { + $this->assertClassHasStaticAttribute('foo', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassNotHasStaticAttributeThrowsException() + { + $this->assertClassNotHasStaticAttribute(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertClassNotHasStaticAttributeThrowsException2() + { + $this->assertClassNotHasStaticAttribute('foo', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertObjectHasAttributeThrowsException() + { + $this->assertObjectHasAttribute(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertObjectHasAttributeThrowsException2() + { + $this->assertObjectHasAttribute('foo', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertObjectNotHasAttributeThrowsException() + { + $this->assertObjectNotHasAttribute(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertObjectNotHasAttributeThrowsException2() + { + $this->assertObjectNotHasAttribute('foo', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassHasAttribute + */ + public function testClassHasPublicAttribute() + { + $this->assertClassHasAttribute('publicAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertClassHasAttribute('attribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassNotHasAttribute + */ + public function testClassNotHasPublicAttribute() + { + $this->assertClassNotHasAttribute('attribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertClassNotHasAttribute('publicAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassHasStaticAttribute + */ + public function testClassHasPublicStaticAttribute() + { + $this->assertClassHasStaticAttribute('publicStaticAttribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertClassHasStaticAttribute('attribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute + */ + public function testClassNotHasPublicStaticAttribute() + { + $this->assertClassNotHasStaticAttribute('attribute', 'ClassWithNonPublicAttributes'); + + try { + $this->assertClassNotHasStaticAttribute('publicStaticAttribute', 'ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + */ + public function testObjectHasPublicAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertObjectHasAttribute('publicAttribute', $obj); + + try { + $this->assertObjectHasAttribute('attribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + */ + public function testObjectNotHasPublicAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertObjectNotHasAttribute('attribute', $obj); + + try { + $this->assertObjectNotHasAttribute('publicAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + */ + public function testObjectHasOnTheFlyAttribute() + { + $obj = new StdClass; + $obj->foo = 'bar'; + + $this->assertObjectHasAttribute('foo', $obj); + + try { + $this->assertObjectHasAttribute('bar', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + */ + public function testObjectNotHasOnTheFlyAttribute() + { + $obj = new StdClass; + $obj->foo = 'bar'; + + $this->assertObjectNotHasAttribute('bar', $obj); + + try { + $this->assertObjectNotHasAttribute('foo', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + */ + public function testObjectHasProtectedAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertObjectHasAttribute('protectedAttribute', $obj); + + try { + $this->assertObjectHasAttribute('attribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + */ + public function testObjectNotHasProtectedAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertObjectNotHasAttribute('attribute', $obj); + + try { + $this->assertObjectNotHasAttribute('protectedAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectHasAttribute + */ + public function testObjectHasPrivateAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertObjectHasAttribute('privateAttribute', $obj); + + try { + $this->assertObjectHasAttribute('attribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertObjectNotHasAttribute + */ + public function testObjectNotHasPrivateAttribute() + { + $obj = new ClassWithNonPublicAttributes; + + $this->assertObjectNotHasAttribute('attribute', $obj); + + try { + $this->assertObjectNotHasAttribute('privateAttribute', $obj); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::attribute + * @covers PHPUnit_Framework_Assert::equalTo + */ + public function testAssertThatAttributeEquals() + { + $this->assertThat( + new ClassWithNonPublicAttributes, + $this->attribute( + $this->equalTo('foo'), + 'publicAttribute' + ) + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::attribute + * @covers PHPUnit_Framework_Assert::equalTo + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertThatAttributeEquals2() + { + $this->assertThat( + new ClassWithNonPublicAttributes, + $this->attribute( + $this->equalTo('bar'), + 'publicAttribute' + ) + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::attribute + * @covers PHPUnit_Framework_Assert::equalTo + */ + public function testAssertThatAttributeEqualTo() + { + $this->assertThat( + new ClassWithNonPublicAttributes, + $this->attributeEqualTo('publicAttribute', 'foo') + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::anything + */ + public function testAssertThatAnything() + { + $this->assertThat('anything', $this->anything()); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::anything + * @covers PHPUnit_Framework_Assert::logicalAnd + */ + public function testAssertThatAnythingAndAnything() + { + $this->assertThat( + 'anything', + $this->logicalAnd( + $this->anything(), $this->anything() + ) + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::anything + * @covers PHPUnit_Framework_Assert::logicalOr + */ + public function testAssertThatAnythingOrAnything() + { + $this->assertThat( + 'anything', + $this->logicalOr( + $this->anything(), $this->anything() + ) + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::anything + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_Assert::logicalXor + */ + public function testAssertThatAnythingXorNotAnything() + { + $this->assertThat( + 'anything', + $this->logicalXor( + $this->anything(), + $this->logicalNot($this->anything()) + ) + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::contains + */ + public function testAssertThatContains() + { + $this->assertThat(array('foo'), $this->contains('foo')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::stringContains + */ + public function testAssertThatStringContains() + { + $this->assertThat('barfoobar', $this->stringContains('foo')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::containsOnly + */ + public function testAssertThatContainsOnly() + { + $this->assertThat(array('foo'), $this->containsOnly('string')); + } + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::containsOnlyInstancesOf + */ + public function testAssertThatContainsOnlyInstancesOf() + { + $this->assertThat(array(new Book), $this->containsOnlyInstancesOf('Book')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::arrayHasKey + */ + public function testAssertThatArrayHasKey() + { + $this->assertThat(array('foo' => 'bar'), $this->arrayHasKey('foo')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::classHasAttribute + */ + public function testAssertThatClassHasAttribute() + { + $this->assertThat( + new ClassWithNonPublicAttributes, + $this->classHasAttribute('publicAttribute') + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::classHasStaticAttribute + */ + public function testAssertThatClassHasStaticAttribute() + { + $this->assertThat( + new ClassWithNonPublicAttributes, + $this->classHasStaticAttribute('publicStaticAttribute') + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::objectHasAttribute + */ + public function testAssertThatObjectHasAttribute() + { + $this->assertThat( + new ClassWithNonPublicAttributes, + $this->objectHasAttribute('publicAttribute') + ); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::equalTo + */ + public function testAssertThatEqualTo() + { + $this->assertThat('foo', $this->equalTo('foo')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::identicalTo + */ + public function testAssertThatIdenticalTo() + { + $value = new StdClass; + $constraint = $this->identicalTo($value); + + $this->assertThat($value, $constraint); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::isInstanceOf + */ + public function testAssertThatIsInstanceOf() + { + $this->assertThat(new StdClass, $this->isInstanceOf('StdClass')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::isType + */ + public function testAssertThatIsType() + { + $this->assertThat('string', $this->isType('string')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::fileExists + */ + public function testAssertThatFileExists() + { + $this->assertThat(__FILE__, $this->fileExists()); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::greaterThan + */ + public function testAssertThatGreaterThan() + { + $this->assertThat(2, $this->greaterThan(1)); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::greaterThanOrEqual + */ + public function testAssertThatGreaterThanOrEqual() + { + $this->assertThat(2, $this->greaterThanOrEqual(1)); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::lessThan + */ + public function testAssertThatLessThan() + { + $this->assertThat(1, $this->lessThan(2)); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::lessThanOrEqual + */ + public function testAssertThatLessThanOrEqual() + { + $this->assertThat(1, $this->lessThanOrEqual(2)); + } + + /** + * @covers PHPUnit_Framework_Assert::assertThat + * @covers PHPUnit_Framework_Assert::matchesRegularExpression + */ + public function testAssertThatMatchesRegularExpression() + { + $this->assertThat('foobar', $this->matchesRegularExpression('/foo/')); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagTypeTrue() + { + $matcher = array('tag' => 'html'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagTypeFalse() + { + $matcher = array('tag' => 'code'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagIdTrue() + { + $matcher = array('id' => 'test_text'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagIdFalse() + { + $matcher = array('id' => 'test_text_doesnt_exist'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagStringContentTrue() + { + $matcher = array('id' => 'test_text', + 'content' => 'My test tag content'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagStringContentFalse() + { + $matcher = array('id' => 'test_text', + 'content' => 'My non existent tag content'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagRegexpContentTrue() + { + $matcher = array('id' => 'test_text', + 'content' => 'regexp:/test tag/'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagRegexpModifierContentTrue() + { + $matcher = array('id' => 'test_text', + 'content' => 'regexp:/TEST TAG/i'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagRegexpContentFalse() + { + $matcher = array('id' => 'test_text', + 'content' => 'regexp:/asdf/'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesTrueA() + { + $matcher = array('tag' => 'span', + 'attributes' => array('class' => 'test_class')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesTrueB() + { + $matcher = array('tag' => 'div', + 'attributes' => array('id' => 'test_child_id')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagAttributesFalse() + { + $matcher = array('tag' => 'span', + 'attributes' => array('class' => 'test_missing_class')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesRegexpTrueA() + { + $matcher = array('tag' => 'span', + 'attributes' => array('class' => 'regexp:/.+_class/')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesRegexpTrueB() + { + $matcher = array('tag' => 'div', + 'attributes' => array('id' => 'regexp:/.+_child_.+/')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesRegexpModifierTrue() + { + $matcher = array('tag' => 'div', + 'attributes' => array('id' => 'regexp:/.+_CHILD_.+/i')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagAttributesRegexpModifierFalse() + { + $matcher = array('tag' => 'div', + 'attributes' => array('id' => 'regexp:/.+_CHILD_.+/')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagAttributesRegexpFalse() + { + $matcher = array('tag' => 'span', + 'attributes' => array('class' => 'regexp:/.+_missing_.+/')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesMultiPartClassTrueA() + { + $matcher = array('tag' => 'div', + 'id' => 'test_multi_class', + 'attributes' => array('class' => 'multi class')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAttributesMultiPartClassTrueB() + { + $matcher = array('tag' => 'div', + 'id' => 'test_multi_class', + 'attributes' => array('class' => 'multi')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagAttributesMultiPartClassFalse() + { + $matcher = array('tag' => 'div', + 'id' => 'test_multi_class', + 'attributes' => array('class' => 'mul')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagParentTrue() + { + $matcher = array('tag' => 'head', + 'parent' => array('tag' => 'html')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagParentFalse() + { + $matcher = array('tag' => 'head', + 'parent' => array('tag' => 'div')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagMultiplePossibleChildren() + { + $matcher = array( + 'tag' => 'li', + 'parent' => array( + 'tag' => 'ul', + 'id' => 'another_ul' + ) + ); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagChildTrue() + { + $matcher = array('tag' => 'html', + 'child' => array('tag' => 'head')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagChildFalse() + { + $matcher = array('tag' => 'html', + 'child' => array('tag' => 'div')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagAncestorTrue() + { + $matcher = array('tag' => 'div', + 'ancestor' => array('tag' => 'html')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagAncestorFalse() + { + $matcher = array('tag' => 'html', + 'ancestor' => array('tag' => 'div')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagDescendantTrue() + { + $matcher = array('tag' => 'html', + 'descendant' => array('tag' => 'div')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagDescendantFalse() + { + $matcher = array('tag' => 'div', + 'descendant' => array('tag' => 'html')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagChildrenCountTrue() + { + $matcher = array('tag' => 'ul', + 'children' => array('count' => 3)); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagChildrenCountFalse() + { + $matcher = array('tag' => 'ul', + 'children' => array('count' => 5)); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagChildrenLessThanTrue() + { + $matcher = array('tag' => 'ul', + 'children' => array('less_than' => 10)); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagChildrenLessThanFalse() + { + $matcher = array('tag' => 'ul', + 'children' => array('less_than' => 2)); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagChildrenGreaterThanTrue() + { + $matcher = array('tag' => 'ul', + 'children' => array('greater_than' => 2)); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagChildrenGreaterThanFalse() + { + $matcher = array('tag' => 'ul', + 'children' => array('greater_than' => 10)); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagChildrenOnlyTrue() + { + $matcher = array('tag' => 'ul', + 'children' => array('only' => array('tag' =>'li'))); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagChildrenOnlyFalse() + { + $matcher = array('tag' => 'ul', + 'children' => array('only' => array('tag' =>'div'))); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagTypeIdTrueA() + { + $matcher = array('tag' => 'ul', 'id' => 'my_ul'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagTypeIdTrueB() + { + $matcher = array('id' => 'my_ul', 'tag' => 'ul'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagTypeIdTrueC() + { + $matcher = array('tag' => 'input', 'id' => 'input_test_id'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertTagTypeIdFalse() + { + $matcher = array('tag' => 'div', 'id' => 'my_ul'); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertTagContentAttributes() + { + $matcher = array('tag' => 'div', + 'content' => 'Test Id Text', + 'attributes' => array('id' => 'test_id', + 'class' => 'my_test_class')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertParentContentAttributes() + { + $matcher = array('tag' => 'div', + 'content' => 'Test Id Text', + 'attributes' => array('id' => 'test_id', + 'class' => 'my_test_class'), + 'parent' => array('tag' => 'body')); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertChildContentAttributes() + { + $matcher = array('tag' => 'div', + 'content' => 'Test Id Text', + 'attributes' => array('id' => 'test_id', + 'class' => 'my_test_class'), + 'child' => array('tag' => 'div', + 'attributes' => array('id' => 'test_child_id'))); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertChildSubChildren() + { + $matcher = array('id' => 'test_id', + 'child' => array('id' => 'test_child_id', + 'child' => array('id' => 'test_subchild_id'))); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertAncestorContentAttributes() + { + $matcher = array('id' => 'test_subchild_id', + 'content' => 'My Subchild', + 'attributes' => array('id' => 'test_subchild_id'), + 'ancestor' => array('tag' => 'div', + 'attributes' => array('id' => 'test_id'))); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertDescendantContentAttributes() + { + $matcher = array('id' => 'test_id', + 'content' => 'Test Id Text', + 'attributes' => array('id' => 'test_id'), + 'descendant' => array('tag' => 'span', + 'attributes' => array('id' => 'test_subchild_id'))); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertTag + */ + public function testAssertChildrenContentAttributes() + { + $matcher = array('id' => 'test_children', + 'content' => 'My Children', + 'attributes' => array('class' => 'children'), + + 'children' => array('less_than' => '25', + 'greater_than' => '2', + 'only' => array('tag' => 'div', + 'attributes' => array('class' => 'my_child')) + )); + $this->assertTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotTag + */ + public function testAssertNotTagTypeIdFalse() + { + $matcher = array('tag' => 'div', 'id' => 'my_ul'); + $this->assertNotTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotTag + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertNotTagContentAttributes() + { + $matcher = array('tag' => 'div', + 'content' => 'Test Id Text', + 'attributes' => array('id' => 'test_id', + 'class' => 'my_test_class')); + $this->assertNotTag($matcher, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountPresentTrue() + { + $selector = 'div#test_id'; + $count = TRUE; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountPresentFalse() + { + $selector = 'div#non_existent'; + $count = TRUE; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountNotPresentTrue() + { + $selector = 'div#non_existent'; + $count = FALSE; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectNotPresentFalse() + { + $selector = 'div#test_id'; + $count = FALSE; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountChildTrue() + { + $selector = '#my_ul > li'; + $count = 3; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountChildFalse() + { + $selector = '#my_ul > li'; + $count = 4; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountDescendantTrue() + { + $selector = '#my_ul li'; + $count = 3; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountDescendantFalse() + { + $selector = '#my_ul li'; + $count = 4; + + $this->assertSelectCount($selector, $count, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountGreaterThanTrue() + { + $selector = '#my_ul > li'; + $range = array('>' => 2); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountGreaterThanFalse() + { + $selector = '#my_ul > li'; + $range = array('>' => 3); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountGreaterThanEqualToTrue() + { + $selector = '#my_ul > li'; + $range = array('>=' => 3); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountGreaterThanEqualToFalse() + { + $selector = '#my_ul > li'; + $range = array('>=' => 4); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountLessThanTrue() + { + $selector = '#my_ul > li'; + $range = array('<' => 4); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountLessThanFalse() + { + $selector = '#my_ul > li'; + $range = array('<' => 3); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountLessThanEqualToTrue() + { + $selector = '#my_ul > li'; + $range = array('<=' => 3); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountLessThanEqualToFalse() + { + $selector = '#my_ul > li'; + $range = array('<=' => 2); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + */ + public function testAssertSelectCountRangeTrue() + { + $selector = '#my_ul > li'; + $range = array('>' => 2, '<' => 4); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectCount + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectCountRangeFalse() + { + $selector = '#my_ul > li'; + $range = array('>' => 1, '<' => 3); + + $this->assertSelectCount($selector, $range, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectEquals + */ + public function testAssertSelectEqualsContentPresentTrue() + { + $selector = 'span.test_class'; + $content = 'Test Class Text'; + + $this->assertSelectEquals($selector, $content, TRUE, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectEquals + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectEqualsContentPresentFalse() + { + $selector = 'span.test_class'; + $content = 'Test Nonexistent'; + + $this->assertSelectEquals($selector, $content, TRUE, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectEquals + */ + public function testAssertSelectEqualsContentNotPresentTrue() + { + $selector = 'span.test_class'; + $content = 'Test Nonexistent'; + + $this->assertSelectEquals($selector, $content, FALSE, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectEquals + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertSelectEqualsContentNotPresentFalse() + { + $selector = 'span.test_class'; + $content = 'Test Class Text'; + + $this->assertSelectEquals($selector, $content, FALSE, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectRegExp + */ + public function testAssertSelectRegExpContentPresentTrue() + { + $selector = 'span.test_class'; + $regexp = '/Test.*Text/'; + + $this->assertSelectRegExp($selector, $regexp, TRUE, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertSelectRegExp + */ + public function testAssertSelectRegExpContentPresentFalse() + { + $selector = 'span.test_class'; + $regexp = '/Nonexistant/'; + + $this->assertSelectRegExp($selector, $regexp, FALSE, $this->html); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFileEquals + */ + public function testAssertFileEquals() + { + $this->assertFileEquals( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'foo.xml' + ); + + try { + $this->assertFileEquals( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'bar.xml' + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertFileNotEquals + */ + public function testAssertFileNotEquals() + { + $this->assertFileNotEquals( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'bar.xml' + ); + + try { + $this->assertFileNotEquals( + $this->filesDirectory . 'foo.xml', + $this->filesDirectory . 'foo.xml' + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEqualsFile + */ + public function testAssertStringEqualsFile() + { + $this->assertStringEqualsFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'foo.xml') + ); + + try { + $this->assertStringEqualsFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'bar.xml') + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringNotEqualsFile + */ + public function testAssertStringNotEqualsFile() + { + $this->assertStringNotEqualsFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'bar.xml') + ); + + try { + $this->assertStringNotEqualsFile( + $this->filesDirectory . 'foo.xml', + file_get_contents($this->filesDirectory . 'foo.xml') + ); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringStartsWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringStartsWithThrowsException() + { + $this->assertStringStartsWith(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringStartsWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringStartsWithThrowsException2() + { + $this->assertStringStartsWith('', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringStartsNotWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringStartsNotWithThrowsException() + { + $this->assertStringStartsNotWith(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringStartsNotWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringStartsNotWithThrowsException2() + { + $this->assertStringStartsNotWith('', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEndsWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringEndsWithThrowsException() + { + $this->assertStringEndsWith(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEndsWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringEndsWithThrowsException2() + { + $this->assertStringEndsWith('', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEndsNotWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringEndsNotWithThrowsException() + { + $this->assertStringEndsNotWith(NULL, NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEndsNotWith + * @expectedException PHPUnit_Framework_Exception + */ + public function testAssertStringEndsNotWithThrowsException2() + { + $this->assertStringEndsNotWith('', NULL); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringStartsWith + */ + public function testAssertStringStartsWith() + { + $this->assertStringStartsWith('prefix', 'prefixfoo'); + + try { + $this->assertStringStartsWith('prefix', 'foo'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringStartsNotWith + */ + public function testAssertStringStartsNotWith() + { + $this->assertStringStartsNotWith('prefix', 'foo'); + + try { + $this->assertStringStartsNotWith('prefix', 'prefixfoo'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEndsWith + */ + public function testAssertStringEndsWith() + { + $this->assertStringEndsWith('suffix', 'foosuffix'); + + try { + $this->assertStringEndsWith('suffix', 'foo'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringEndsNotWith + */ + public function testAssertStringEndsNotWith() + { + $this->assertStringEndsNotWith('suffix', 'foo'); + + try { + $this->assertStringEndsNotWith('suffix', 'foosuffix'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringMatchesFormat + */ + public function testAssertStringMatchesFormat() + { + $this->assertStringMatchesFormat('*%s*', '***'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringMatchesFormat + * @expectedException PHPUnit_Framework_AssertionFailedError + */ + public function testAssertStringMatchesFormatFailure() + { + $this->assertStringMatchesFormat('*%s*', '**'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertStringNotMatchesFormat + */ + public function testAssertStringNotMatchesFormat() + { + $this->assertStringNotMatchesFormat('*%s*', '**'); + + try { + $this->assertStringMatchesFormat('*%s*', '**'); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertEmpty + */ + public function testAssertEmpty() + { + $this->assertEmpty(array()); + + try { + $this->assertEmpty(array('foo')); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertNotEmpty + */ + public function testAssertNotEmpty() + { + $this->assertNotEmpty(array('foo')); + + try { + $this->assertNotEmpty(array()); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeEmpty + */ + public function testAssertAttributeEmpty() + { + $o = new StdClass; + $o->a = array(); + + $this->assertAttributeEmpty('a', $o); + + try { + $o->a = array('b'); + $this->assertAttributeEmpty('a', $o); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertAttributeNotEmpty + */ + public function testAssertAttributeNotEmpty() + { + $o = new StdClass; + $o->a = array('b'); + + $this->assertAttributeNotEmpty('a', $o); + + try { + $o->a = array(); + $this->assertAttributeNotEmpty('a', $o); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::markTestIncomplete + */ + public function testMarkTestIncomplete() + { + try { + $this->markTestIncomplete('incomplete'); + } + + catch (PHPUnit_Framework_IncompleteTestError $e) { + $this->assertEquals('incomplete', $e->getMessage()); + + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::markTestSkipped + */ + public function testMarkTestSkipped() + { + try { + $this->markTestSkipped('skipped'); + } + + catch (PHPUnit_Framework_SkippedTestError $e) { + $this->assertEquals('skipped', $e->getMessage()); + + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertCount + */ + public function testAssertCount() + { + $this->assertCount(2, array(1,2)); + + try { + $this->assertCount(2, array(1,2,3)); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertCount + */ + public function testAssertCountThrowsExceptionIfExpectedCountIsNoInteger() + { + + try { + $this->assertCount('a', array()); + } + + catch (PHPUnit_Framework_Exception $e) { + $this->assertEquals('Argument #1 of PHPUnit_Framework_Assert::assertCount() must be a integer', $e->getMessage()); + + return; + } + + $this->fail(); + } + + + /** + * @covers PHPUnit_Framework_Assert::assertCount + */ + public function testAssertCountThrowsExceptionIfElementIsNotCountable() + { + + try { + $this->assertCount(2, ''); + } + + catch (PHPUnit_Framework_Exception $e) { + $this->assertEquals('Argument #2 of PHPUnit_Framework_Assert::assertCount() must be a countable', $e->getMessage()); + + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringEqualsJsonString + */ + public function testAssertJsonStringEqualsJsonString() + { + $expected = '{"Mascott" : "Tux"}'; + $actual = '{"Mascott" : "Tux"}'; + $message = 'Given Json strings do not match'; + + $this->assertJsonStringEqualsJsonString($expected, $actual, $message); + } + + /** + * @dataProvider validInvalidJsonDataprovider + * @covers PHPUnit_Framework_Assert::assertJsonStringEqualsJsonString + */ + public function testAssertJsonStringEqualsJsonStringErrorRaised($expected, $actual) + { + try { + $this->assertJsonStringEqualsJsonString($expected, $actual); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + return; + } + $this->fail('Expected exception not found'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonString + */ + public function testAssertJsonStringNotEqualsJsonString() + { + $expected = '{"Mascott" : "Beastie"}'; + $actual = '{"Mascott" : "Tux"}'; + $message = 'Given Json strings do match'; + + $this->assertJsonStringNotEqualsJsonString($expected, $actual, $message); + } + + /** + * @dataProvider validInvalidJsonDataprovider + * @covers PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonString + */ + public function testAssertJsonStringNotEqualsJsonStringErrorRaised($expected, $actual) + { + $this->assertJsonStringNotEqualsJsonString($expected, $actual); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile + */ + public function testAssertJsonStringEqualsJsonFile() + { + $file = __DIR__ . '/../_files/JsonData/simpleObject.js'; + $actual = json_encode(array("Mascott" => "Tux")); + $message = ''; + $this->assertJsonStringEqualsJsonFile($file, $actual, $message); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile + */ + public function testAssertJsonStringEqualsJsonFileExpectingExpectationFailedException() + { + $file = __DIR__ . '/../_files/JsonData/simpleObject.js'; + $actual = json_encode(array("Mascott" => "Beastie")); + $message = ''; + try { + $this->assertJsonStringEqualsJsonFile($file, $actual, $message); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + 'Failed asserting that \'{"Mascott":"Beastie"}\' matches JSON string "{"Mascott":"Tux"}".', + $e->getMessage() + ); + return; + } + + $this->fail('Expected Exception not thrown.'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile + */ + public function testAssertJsonStringEqualsJsonFileExpectingException() + { + $file = __DIR__ . '/../_files/JsonData/simpleObject.js'; + try { + $this->assertJsonStringEqualsJsonFile($file, NULL); + } catch (PHPUnit_Framework_Exception $e) { + return; + } + $this->fail('Expected Exception not thrown.'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonFile + */ + public function testAssertJsonStringNotEqualsJsonFile() + { + $file = __DIR__ . '/../_files/JsonData/simpleObject.js'; + $actual = json_encode(array("Mascott" => "Beastie")); + $message = ''; + $this->assertJsonStringNotEqualsJsonFile($file, $actual, $message); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonFile + */ + public function testAssertJsonStringNotEqualsJsonFileExpectingException() + { + $file = __DIR__ . '/../_files/JsonData/simpleObject.js'; + try { + $this->assertJsonStringNotEqualsJsonFile($file, NULL); + } catch (PHPUnit_Framework_Exception $e) { + return; + } + $this->fail('Expected exception not found.'); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonFileNotEqualsJsonFile + */ + public function testAssertJsonFileNotEqualsJsonFile() + { + $fileExpected = __DIR__ . '/../_files/JsonData/simpleObject.js'; + $fileActual = __DIR__ . '/../_files/JsonData/arrayObject.js'; + $message = ''; + $this->assertJsonFileNotEqualsJsonFile($fileExpected, $fileActual, $message); + } + + /** + * @covers PHPUnit_Framework_Assert::assertJsonFileEqualsJsonFile + */ + public function testAssertJsonFileEqualsJsonFile() + { + $file = __DIR__ . '/../_files/JsonData/simpleObject.js'; + $message = ''; + $this->assertJsonFileEqualsJsonFile($file, $file, $message); + } + + public static function validInvalidJsonDataprovider() + { + return array( + 'error syntax in expected JSON' => array('{"Mascott"::}', '{"Mascott" : "Tux"}'), + 'error UTF-8 in actual JSON' => array('{"Mascott" : "Tux"}', '{"Mascott" : :}'), + ); + } + +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/ComparatorTest.php b/vendor/phpunit/phpunit/Tests/Framework/ComparatorTest.php new file mode 100644 index 0000000..13da095 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/ComparatorTest.php @@ -0,0 +1,159 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ClassWithToString.php'; + +class TestClass {} +class TestClassComparator extends PHPUnit_Framework_Comparator_Object {} + +/** + * + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class Framework_ComparatorTest extends PHPUnit_Framework_TestCase +{ + // Don't use other test methods than ->fail() here, because the testers tested + // here are the foundation for the other test methods + + public function instanceProvider() + { + $tmpfile = tmpfile(); + + return array( + array(NULL, NULL, 'PHPUnit_Framework_Comparator_Scalar'), + array(NULL, TRUE, 'PHPUnit_Framework_Comparator_Scalar'), + array(TRUE, NULL, 'PHPUnit_Framework_Comparator_Scalar'), + array(TRUE, TRUE, 'PHPUnit_Framework_Comparator_Scalar'), + array(FALSE, FALSE, 'PHPUnit_Framework_Comparator_Scalar'), + array(TRUE, FALSE, 'PHPUnit_Framework_Comparator_Scalar'), + array(FALSE, TRUE, 'PHPUnit_Framework_Comparator_Scalar'), + array('', '', 'PHPUnit_Framework_Comparator_Scalar'), + array('0', '0', 'PHPUnit_Framework_Comparator_Numeric'), + array('0', 0, 'PHPUnit_Framework_Comparator_Numeric'), + array(0, '0', 'PHPUnit_Framework_Comparator_Numeric'), + array(0, 0, 'PHPUnit_Framework_Comparator_Numeric'), + array(1.0, 0, 'PHPUnit_Framework_Comparator_Double'), + array(0, 1.0, 'PHPUnit_Framework_Comparator_Double'), + array(1.0, 1.0, 'PHPUnit_Framework_Comparator_Double'), + array(array(1), array(1), 'PHPUnit_Framework_Comparator_Array'), + array($tmpfile, $tmpfile, 'PHPUnit_Framework_Comparator_Resource'), + array(new stdClass, new stdClass, 'PHPUnit_Framework_Comparator_Object'), + array(new SplObjectStorage, new SplObjectStorage, 'PHPUnit_Framework_Comparator_SplObjectStorage'), + array(new Exception, new Exception, 'PHPUnit_Framework_Comparator_Exception'), + array(new DOMDocument, new DOMDocument, 'PHPUnit_Framework_Comparator_DOMDocument'), + // mixed types + array($tmpfile, array(1), 'PHPUnit_Framework_Comparator_Type'), + array(array(1), $tmpfile, 'PHPUnit_Framework_Comparator_Type'), + array($tmpfile, '1', 'PHPUnit_Framework_Comparator_Type'), + array('1', $tmpfile, 'PHPUnit_Framework_Comparator_Type'), + array($tmpfile, new stdClass, 'PHPUnit_Framework_Comparator_Type'), + array(new stdClass, $tmpfile, 'PHPUnit_Framework_Comparator_Type'), + array(new stdClass, array(1), 'PHPUnit_Framework_Comparator_Type'), + array(array(1), new stdClass, 'PHPUnit_Framework_Comparator_Type'), + array(new stdClass, '1', 'PHPUnit_Framework_Comparator_Type'), + array('1', new stdClass, 'PHPUnit_Framework_Comparator_Type'), + array(new ClassWithToString, '1', 'PHPUnit_Framework_Comparator_Scalar'), + array('1', new ClassWithToString, 'PHPUnit_Framework_Comparator_Scalar'), + array(1.0, new stdClass, 'PHPUnit_Framework_Comparator_Type'), + array(new stdClass, 1.0, 'PHPUnit_Framework_Comparator_Type'), + array(1.0, array(1), 'PHPUnit_Framework_Comparator_Type'), + array(array(1), 1.0, 'PHPUnit_Framework_Comparator_Type'), + ); + } + + /** + * @dataProvider instanceProvider + */ + public function testGetInstance($a, $b, $expected) + { + $factory = new PHPUnit_Framework_ComparatorFactory; + + if (get_class($factory->getComparatorFor($a, $b)) != $expected) { + $this->fail(); + } + } + + public function testRegister() + { + $comparator = new TestClassComparator; + + $factory = new PHPUnit_Framework_ComparatorFactory; + $factory->register($comparator); + + $a = new TestClass; + $b = new TestClass; + $expected = 'TestClassComparator'; + + if (get_class($factory->getComparatorFor($a, $b)) != $expected) { + $factory->unregister($comparator); + $this->fail(); + } + + $factory->unregister($comparator); + } + + public function testUnregister() + { + $comparator = new TestClassComparator; + + $factory = new PHPUnit_Framework_ComparatorFactory; + $factory->register($comparator); + $factory->unregister($comparator); + + $a = new TestClass; + $b = new TestClass; + $expected = 'PHPUnit_Framework_Comparator_Object'; + + if (get_class($factory->getComparatorFor($a, $b)) != $expected) { + var_dump(get_class($factory->getComparatorFor($a, $b))); + $this->fail(); + } + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatches/ErrorMessageProviderTest.php b/vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatches/ErrorMessageProviderTest.php new file mode 100644 index 0000000..79d1bf2 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatches/ErrorMessageProviderTest.php @@ -0,0 +1,122 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bastian Feder + * @copyright 2002-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since File available since Release 3.7.0 + */ + +/** + * @package PHPUnit + * @author Bastian Feder + * @copyright 2011-2012 Bastian Feder + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since File available since Release 3.7.0 + */ +class Framework_Constraint_JsonMatches_ErrorMessageProviderTest extends PHPUnit_Framework_TestCase +{ + /** + * @dataProvider translateTypeToPrefixDataprovider + * @covers PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::translateTypeToPrefix + */ + public function testTranslatTypeToPrefix($expected, $type) + { + $this->assertEquals( + $expected, + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::translateTypeToPrefix($type) + ); + } + + /** + * @dataProvider determineJsonErrorDataprovider + * @covers PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError + */ + public function testDetermineJsonError($expected, $error, $prefix) + { + $this->assertEquals( + $expected, + PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + $error, + $prefix + ) + ); + } + + public static function determineJsonErrorDataprovider() + { + return array( + 'JSON_ERROR_NONE' => array( + NULL, 'json_error_none', '' + ), + 'JSON_ERROR_DEPTH' => array( + 'Maximum stack depth exceeded', 'json_error_depth', '' + ), + 'prefixed JSON_ERROR_DEPTH' => array( + 'TUX: Maximum stack depth exceeded', 'json_error_depth', 'TUX: ' + ), + 'JSON_ERROR_STATE_MISMatch' => array( + 'Underflow or the modes mismatch', 'json_error_state_mismatch', '' + ), + 'JSON_ERROR_CTRL_CHAR' => array( + 'Unexpected control character found', 'json_error_ctrl_char', '' + ), + 'JSON_ERROR_SYNTAX' => array( + 'Syntax error, malformed JSON', 'json_error_syntax', '' + ), + 'JSON_ERROR_UTF8`' => array( + 'Malformed UTF-8 characters, possibly incorrectly encoded', + 'json_error_utf8', + '' + ), + 'Invalid error indicator' => array( + 'Unknown error', 'invalid_error_indicator', '' + ), + ); + } + + public static function translateTypeToPrefixDataprovider() + { + return array( + 'expected' => array('Expected value JSON decode error - ', 'expected'), + 'actual' => array('Actual value JSON decode error - ', 'actual'), + 'default' => array('', ''), + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatchesTest.php b/vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatchesTest.php new file mode 100644 index 0000000..de656aa --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/Constraint/JsonMatchesTest.php @@ -0,0 +1,90 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bastian Feder + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since File available since Release 3.7.0 + */ + +/** + * @package PHPUnit + * @author Bastian Feder + * @copyright 2011 Bastian Feder + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause + * @link http://www.phpunit.de/ + * @since File available since Release 3.7.0 + */ +class Framework_Constraint_JsonMatchesTest extends PHPUnit_Framework_TestCase +{ + + /** + * @dataProvider evaluateDataprovider + * @covers PHPUnit_Framework_Constraint_JsonMatches::evaluate + * @covers PHPUnit_Framework_Constraint_JsonMatches::matches + * @covers PHPUnit_Framework_Constraint_JsonMatches::__construct + * @covers PHPUnit_Framework_Constraint_JsonMatches::getJsonError + */ + public function testEvaluate($expected, $jsonOther, $jsonValue) + { + $constraint = new PHPUnit_Framework_Constraint_JsonMatches($jsonValue); + $this->assertEquals($expected, $constraint->evaluate($jsonOther, '', TRUE)); + } + + /** + * @covers PHPUnit_Framework_Constraint_JsonMatches::toString + */ + public function testToString() + { + $jsonValue = json_encode(array('Mascott' => 'Tux')); + $constraint = new PHPUnit_Framework_Constraint_JsonMatches($jsonValue); + + $this->assertEquals('matches JSON string "' . $jsonValue . '"', $constraint->toString()); + } + + + public static function evaluateDataprovider() + { + return array( + 'valid JSON' => array(TRUE, json_encode(array('Mascott' => 'Tux')), json_encode(array('Mascott' => 'Tux'))), + 'error syntax' => array(FALSE, '{"Mascott"::}', json_encode(array('Mascott' => 'Tux'))), + 'error UTF-8' => array(FALSE, json_encode('\xB1\x31'), json_encode(array('Mascott' => 'Tux'))), + 'invalid JSON in class instantiation' => array(FALSE, json_encode(array('Mascott' => 'Tux')), '{"Mascott"::}'), + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/ConstraintTest.php b/vendor/phpunit/phpunit/Tests/Framework/ConstraintTest.php new file mode 100644 index 0000000..5d8b928 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/ConstraintTest.php @@ -0,0 +1,3554 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ClassWithNonPublicAttributes.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'TestIterator.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.0.0 + */ +class Framework_ConstraintTest extends PHPUnit_Framework_TestCase +{ + /** + * Removes spaces in front of newlines + * + * @param string $string + * @return string + */ + public static function trimnl($string) + { + return preg_replace('/[ ]*\n/', "\n", $string); + } + + /** + * @covers PHPUnit_Framework_Constraint_ArrayHasKey + * @covers PHPUnit_Framework_Assert::arrayHasKey + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayHasKey() + { + $constraint = PHPUnit_Framework_Assert::arrayHasKey(0); + + $this->assertFalse($constraint->evaluate(array(), '', TRUE)); + $this->assertEquals('has the key 0', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(array()); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ArrayHasKey + * @covers PHPUnit_Framework_Assert::arrayHasKey + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayHasKey2() + { + $constraint = PHPUnit_Framework_Assert::arrayHasKey(0); + + try { + $constraint->evaluate(array(), 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ArrayHasKey + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::arrayHasKey + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayNotHasKey() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::arrayHasKey(0) + ); + + $this->assertFalse($constraint->evaluate(array(0 => 1), '', TRUE)); + $this->assertEquals('does not have the key 0', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(array(0 => 1)); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ArrayHasKey + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::arrayHasKey + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayNotHasKey2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::arrayHasKey(0) + ); + + try { + $constraint->evaluate(array(0), 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_FileExists + * @covers PHPUnit_Framework_Assert::fileExists + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintFileExists() + { + $constraint = PHPUnit_Framework_Assert::fileExists(); + + $this->assertFalse($constraint->evaluate('foo', '', TRUE)); + $this->assertEquals('file exists', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('foo'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_FileExists + * @covers PHPUnit_Framework_Assert::fileExists + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintFileExists2() + { + $constraint = PHPUnit_Framework_Assert::fileExists(); + + try { + $constraint->evaluate('foo', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_FileExists + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_Assert::fileExists + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintFileNotExists() + { + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ClassWithNonPublicAttributes.php'; + + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::fileExists() + ); + + $this->assertFalse($constraint->evaluate($file, '', TRUE)); + $this->assertEquals('file does not exist', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate($file); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_FileExists + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_Assert::fileExists + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintFileNotExists2() + { + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ClassWithNonPublicAttributes.php'; + + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::fileExists() + ); + + try { + $constraint->evaluate($file, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Assert::greaterThan + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintGreaterThan() + { + $constraint = PHPUnit_Framework_Assert::greaterThan(1); + + $this->assertFalse($constraint->evaluate(0, '', TRUE)); + $this->assertTrue($constraint->evaluate(2, '', TRUE)); + $this->assertEquals('is greater than 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(0); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Assert::greaterThan + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintGreaterThan2() + { + $constraint = PHPUnit_Framework_Assert::greaterThan(1); + + try { + $constraint->evaluate(0, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::greaterThan + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotGreaterThan() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::greaterThan(1) + ); + + $this->assertTrue($constraint->evaluate(1, '', TRUE)); + $this->assertEquals('is not greater than 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(2); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::greaterThan + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotGreaterThan2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::greaterThan(1) + ); + + try { + $constraint->evaluate(2, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Assert::greaterThanOrEqual + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintGreaterThanOrEqual() + { + $constraint = PHPUnit_Framework_Assert::greaterThanOrEqual(1); + + $this->assertTrue($constraint->evaluate(1, '', TRUE)); + $this->assertFalse($constraint->evaluate(0, '', TRUE)); + $this->assertEquals('is equal to 1 or is greater than 1', $constraint->toString()); + $this->assertEquals(2, count($constraint)); + + try { + $constraint->evaluate(0); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Assert::greaterThanOrEqual + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintGreaterThanOrEqual2() + { + $constraint = PHPUnit_Framework_Assert::greaterThanOrEqual(1); + + try { + $constraint->evaluate(0, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::greaterThanOrEqual + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotGreaterThanOrEqual() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::greaterThanOrEqual(1) + ); + + $this->assertFalse($constraint->evaluate(1, '', TRUE)); + $this->assertEquals('not( is equal to 1 or is greater than 1 )', $constraint->toString()); + $this->assertEquals(2, count($constraint)); + + try { + $constraint->evaluate(1); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_GreaterThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::greaterThanOrEqual + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotGreaterThanOrEqual2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::greaterThanOrEqual(1) + ); + + try { + $constraint->evaluate(1, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsAnything + * @covers PHPUnit_Framework_Assert::anything + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsAnything() + { + $constraint = PHPUnit_Framework_Assert::anything(); + + $this->assertTrue($constraint->evaluate(NULL, '', TRUE)); + $this->assertNull($constraint->evaluate(NULL)); + $this->assertEquals('is anything', $constraint->toString()); + $this->assertEquals(0, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsAnything + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::anything + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotIsAnything() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::anything() + ); + + $this->assertFalse($constraint->evaluate(NULL, '', TRUE)); + $this->assertEquals('is not anything', $constraint->toString()); + $this->assertEquals(0, count($constraint)); + + try { + $constraint->evaluate(NULL); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Assert::equalTo + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsEqual() + { + $constraint = PHPUnit_Framework_Assert::equalTo(1); + + $this->assertTrue($constraint->evaluate(1, '', TRUE)); + $this->assertFalse($constraint->evaluate(0, '', TRUE)); + $this->assertEquals('is equal to 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(0); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + public function isEqualProvider() + { + $a = new stdClass; + $a->foo = 'bar'; + $b = new stdClass; + $ahash = spl_object_hash($a); + $bhash = spl_object_hash($b); + + $c = new stdClass; + $c->foo = 'bar'; + $c->int = 1; + $c->array = array(0, array(1), array(2), 3); + $c->related = new stdClass; + $c->related->foo = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk"; + $c->self = $c; + $c->c = $c; + $d = new stdClass; + $d->foo = 'bar'; + $d->int = 2; + $d->array = array(0, array(4), array(2), 3); + $d->related = new stdClass; + $d->related->foo = "a\np\nc\nd\ne\nf\ng\nh\ni\nw\nk"; + $d->self = $d; + $d->c = $c; + + $storage1 = new SplObjectStorage; + $storage1->attach($a); + $storage1->attach($b); + $storage2 = new SplObjectStorage; + $storage2->attach($b); + + $dom1 = new DOMDocument; + $dom1->preserveWhiteSpace = FALSE; + $dom1->loadXML(''); + $dom2 = new DOMDocument; + $dom2->preserveWhiteSpace = FALSE; + $dom2->loadXML(''); + + return array( + array(1, 0, << 0 ++ 0 => 1 + ) + +EOF + ), + array(array(TRUE), array('true'), << true ++ 0 => 'true' + ) + +EOF + ), + array(array(0, array(1), array(2), 3), array(0, array(4), array(2), 3), << 0 + 1 => Array ( +- 0 => 1 ++ 0 => 4 + ) + 2 => Array (...) + 3 => 3 + ) + +EOF + ), + array($a, array(0), << 'bar' + ) + +EOF + ), + array($c, $d, << 'bar' +- 'int' => 1 ++ 'int' => 2 + 'array' => Array ( + 0 => 0 + 1 => Array ( +- 0 => 1 ++ 0 => 4 + +@@ @@ + 'foo' => 'a +- b ++ p + +@@ @@ + i +- j ++ w + k' + ) + 'self' => stdClass Object (...) + 'c' => stdClass Object (...) + ) + +EOF + ), + array($storage1, $storage2, << Array ( +- 'obj' => stdClass Object ( +- 'foo' => 'bar' +- ) +- 'inf' => null +- ) + '$bhash' => Array ( + 'obj' => stdClass Object () + 'inf' => null + ) + ) + +EOF + ), + array($dom1, $dom2, << +- ++ ++ ++ + +EOF + ), + ); + } + + /** + * @dataProvider isEqualProvider + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Assert::equalTo + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsEqual2($expected, $actual, $message) + { + $constraint = PHPUnit_Framework_Assert::equalTo($expected); + + try { + $constraint->evaluate($actual, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + "custom message\n$message", + self::trimnl(PHPUnit_Framework_TestFailure::exceptionToString($e)) + ); + + return; + } + + $this->fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::equalTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotEqual() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::equalTo(1) + ); + + $this->assertTrue($constraint->evaluate(0, '', TRUE)); + $this->assertFalse($constraint->evaluate(1, '', TRUE)); + $this->assertEquals('is not equal to 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(1); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::equalTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotEqual2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::equalTo(1) + ); + + try { + $constraint->evaluate(1, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsIdentical + * @covers PHPUnit_Framework_Assert::identicalTo + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsIdentical() + { + $a = new stdClass; + $b = new stdClass; + + $constraint = PHPUnit_Framework_Assert::identicalTo($a); + + $this->assertFalse($constraint->evaluate($b, '', TRUE)); + $this->assertTrue($constraint->evaluate($a, '', TRUE)); + $this->assertEquals('is identical to an object of class "stdClass"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate($b); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsIdentical + * @covers PHPUnit_Framework_Assert::identicalTo + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsIdentical2() + { + $a = new stdClass; + $b = new stdClass; + + $constraint = PHPUnit_Framework_Assert::identicalTo($a); + + try { + $constraint->evaluate($b, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsIdentical + * @covers PHPUnit_Framework_Assert::identicalTo + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsIdentical3() + { + $constraint = PHPUnit_Framework_Assert::identicalTo('a'); + + try { + $constraint->evaluate('b', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsIdentical + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::identicalTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotIdentical() + { + $a = new stdClass; + $b = new stdClass; + + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::identicalTo($a) + ); + + $this->assertTrue($constraint->evaluate($b, '', TRUE)); + $this->assertFalse($constraint->evaluate($a, '', TRUE)); + $this->assertEquals('is not identical to an object of class "stdClass"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate($a); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsIdentical + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::identicalTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotIdentical2() + { + $a = new stdClass; + + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::identicalTo($a) + ); + + try { + $constraint->evaluate($a, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsIdentical + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::identicalTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotIdentical3() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::identicalTo('a') + ); + + try { + $constraint->evaluate('a', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsInstanceOf + * @covers PHPUnit_Framework_Assert::isInstanceOf + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsInstanceOf() + { + $constraint = PHPUnit_Framework_Assert::isInstanceOf('Exception'); + + $this->assertFalse($constraint->evaluate(new stdClass, '', TRUE)); + $this->assertTrue($constraint->evaluate(new Exception, '', TRUE)); + $this->assertEquals('is instance of class "Exception"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(new stdClass); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsInstanceOf + * @covers PHPUnit_Framework_Assert::isInstanceOf + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsInstanceOf2() + { + $constraint = PHPUnit_Framework_Assert::isInstanceOf('Exception'); + + try { + $constraint->evaluate(new stdClass, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsInstanceOf + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::isInstanceOf + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotInstanceOf() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::isInstanceOf('stdClass') + ); + + $this->assertFalse($constraint->evaluate(new stdClass, '', TRUE)); + $this->assertTrue($constraint->evaluate(new Exception, '', TRUE)); + $this->assertEquals('is not instance of class "stdClass"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(new stdClass); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsInstanceOf + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::isInstanceOf + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotInstanceOf2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::isInstanceOf('stdClass') + ); + + try { + $constraint->evaluate(new stdClass, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsType + * @covers PHPUnit_Framework_Assert::isType + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsType() + { + $constraint = PHPUnit_Framework_Assert::isType('string'); + + $this->assertFalse($constraint->evaluate(0, '', TRUE)); + $this->assertTrue($constraint->evaluate('', '', TRUE)); + $this->assertEquals('is of type "string"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(new stdClass); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsType + * @covers PHPUnit_Framework_Assert::isType + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsType2() + { + $constraint = PHPUnit_Framework_Assert::isType('string'); + + try { + $constraint->evaluate(new stdClass, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsType + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::isType + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotType() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::isType('string') + ); + + $this->assertTrue($constraint->evaluate(0, '', TRUE)); + $this->assertFalse($constraint->evaluate('', '', TRUE)); + $this->assertEquals('is not of type "string"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(''); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsType + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::isType + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotType2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::isType('string') + ); + + try { + $constraint->evaluate('', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsNull + * @covers PHPUnit_Framework_Assert::isNull + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNull() + { + $constraint = PHPUnit_Framework_Assert::isNull(); + + $this->assertFalse($constraint->evaluate(0, '', TRUE)); + $this->assertTrue($constraint->evaluate(NULL, '', TRUE)); + $this->assertEquals('is null', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(0); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsNull + * @covers PHPUnit_Framework_Assert::isNull + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNull2() + { + $constraint = PHPUnit_Framework_Assert::isNull(); + + try { + $constraint->evaluate(0, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsNull + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::isNull + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotNull() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::isNull() + ); + + $this->assertFalse($constraint->evaluate(NULL, '', TRUE)); + $this->assertTrue($constraint->evaluate(0, '', TRUE)); + $this->assertEquals('is not null', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(NULL); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsNull + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::isNull + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsNotNull2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::isNull() + ); + + try { + $constraint->evaluate(NULL, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Assert::lessThan + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintLessThan() + { + $constraint = PHPUnit_Framework_Assert::lessThan(1); + + $this->assertTrue($constraint->evaluate(0, '', TRUE)); + $this->assertFalse($constraint->evaluate(1, '', TRUE)); + $this->assertEquals('is less than 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(1); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Assert::lessThan + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintLessThan2() + { + $constraint = PHPUnit_Framework_Assert::lessThan(1); + + try { + $constraint->evaluate(1, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::lessThan + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotLessThan() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::lessThan(1) + ); + + $this->assertTrue($constraint->evaluate(1, '', TRUE)); + $this->assertFalse($constraint->evaluate(0, '', TRUE)); + $this->assertEquals('is not less than 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(0); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::lessThan + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotLessThan2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::lessThan(1) + ); + + try { + $constraint->evaluate(0, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Assert::lessThanOrEqual + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintLessThanOrEqual() + { + $constraint = PHPUnit_Framework_Assert::lessThanOrEqual(1); + + $this->assertTrue($constraint->evaluate(1, '', TRUE)); + $this->assertFalse($constraint->evaluate(2, '', TRUE)); + $this->assertEquals('is equal to 1 or is less than 1', $constraint->toString()); + $this->assertEquals(2, count($constraint)); + + try { + $constraint->evaluate(2); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_Callback + */ + public function testConstraintCallback() + { + $closureReflect = function($parameter) { + return $parameter; + }; + + $closureWithoutParameter = function() { + return TRUE; + }; + + $constraint = PHPUnit_Framework_Assert::callback($closureWithoutParameter); + $this->assertTrue($constraint->evaluate('', '', TRUE)); + + $constraint = PHPUnit_Framework_Assert::callback($closureReflect); + $this->assertTrue($constraint->evaluate(TRUE, '', TRUE)); + $this->assertFalse($constraint->evaluate(FALSE, '', TRUE)); + + $callback = array($this, 'callbackReturningTrue'); + $constraint = PHPUnit_Framework_Assert::callback($callback); + $this->assertTrue($constraint->evaluate(FALSE, '', TRUE)); + + $callback = array('Framework_ConstraintTest', 'staticCallbackReturningTrue'); + $constraint = PHPUnit_Framework_Assert::callback($callback); + $this->assertTrue($constraint->evaluate(NULL, '', TRUE)); + + $this->assertEquals('is accepted by specified callback', $constraint->toString()); + } + + /** + * @covers PHPUnit_Framework_Constraint_Callback + * @expectedException PHPUnit_Framework_ExpectationFailedException + * @expectedExceptionMessage Failed asserting that 'This fails' is accepted by specified callback. + */ + public function testConstraintCallbackFailure() + { + $constraint = PHPUnit_Framework_Assert::callback(function() { + return FALSE; + }); + $constraint->evaluate('This fails'); + } + + public function callbackReturningTrue() + { + return TRUE; + } + + public static function staticCallbackReturningTrue() + { + return TRUE; + } + + /** + * @covers PHPUnit_Framework_Constraint_Callback + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Specified callback is not callable. + */ + public function testConstraintCallbackInvalidFunctionArgument() + { + PHPUnit_Framework_Assert::callback('invalid callback'); + } + + /** + * @covers PHPUnit_Framework_Constraint_Callback + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Specified callback is not callable. + */ + public function testConstraintCallbackInvalidArrayArgumentWithEmptyArray() + { + PHPUnit_Framework_Assert::callback(array()); + } + + /** + * @covers PHPUnit_Framework_Constraint_Callback + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Specified callback is not callable. + */ + public function testConstraintCallbackInvalidArrayArgumentWithBadArray() + { + PHPUnit_Framework_Assert::callback(array(3 => 'foo')); + } + + + /** + * @covers PHPUnit_Framework_Constraint_Callback + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Specified callback is not callable. + */ + public function testConstraintCallbackInvalidArrayArgumentWithObject() + { + PHPUnit_Framework_Assert::callback(array($this, 'invalid callback')); + } + + /** + * @covers PHPUnit_Framework_Constraint_Callback + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Specified callback is not callable. + */ + public function testConstraintCallbackInvalidArrayArgumentWithClassname() + { + PHPUnit_Framework_Assert::callback(array('Framework_ConstraintTest', 'invalid callback')); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Assert::lessThanOrEqual + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintLessThanOrEqual2() + { + $constraint = PHPUnit_Framework_Assert::lessThanOrEqual(1); + + try { + $constraint->evaluate(2, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::lessThanOrEqual + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotLessThanOrEqual() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::lessThanOrEqual(1) + ); + + $this->assertTrue($constraint->evaluate(2, '', TRUE)); + $this->assertFalse($constraint->evaluate(1, '', TRUE)); + $this->assertEquals('not( is equal to 1 or is less than 1 )', $constraint->toString()); + $this->assertEquals(2, count($constraint)); + + try { + $constraint->evaluate(1); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEqual + * @covers PHPUnit_Framework_Constraint_LessThan + * @covers PHPUnit_Framework_Constraint_Or + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::lessThanOrEqual + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotLessThanOrEqual2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::lessThanOrEqual(1) + ); + + try { + $constraint->evaluate(1, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasAttribute + * @covers PHPUnit_Framework_Assert::classHasAttribute + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassHasAttribute() + { + $constraint = PHPUnit_Framework_Assert::classHasAttribute('privateAttribute'); + + $this->assertTrue($constraint->evaluate('ClassWithNonPublicAttributes', '', TRUE)); + $this->assertFalse($constraint->evaluate('stdClass', '', TRUE)); + $this->assertEquals('has attribute "privateAttribute"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('stdClass'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasAttribute + * @covers PHPUnit_Framework_Assert::classHasAttribute + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassHasAttribute2() + { + $constraint = PHPUnit_Framework_Assert::classHasAttribute('privateAttribute'); + + try { + $constraint->evaluate('stdClass', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasAttribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::classHasAttribute + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassNotHasAttribute() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::classHasAttribute('privateAttribute') + ); + + $this->assertTrue($constraint->evaluate('stdClass', '', TRUE)); + $this->assertFalse($constraint->evaluate('ClassWithNonPublicAttributes', '', TRUE)); + $this->assertEquals('does not have attribute "privateAttribute"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasAttribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::classHasAttribute + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassNotHasAttribute2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::classHasAttribute('privateAttribute') + ); + + try { + $constraint->evaluate('ClassWithNonPublicAttributes', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * @covers PHPUnit_Framework_Assert::classHasStaticAttribute + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassHasStaticAttribute() + { + $constraint = PHPUnit_Framework_Assert::classHasStaticAttribute('privateStaticAttribute'); + + $this->assertTrue($constraint->evaluate('ClassWithNonPublicAttributes', '', TRUE)); + $this->assertFalse($constraint->evaluate('stdClass', '', TRUE)); + $this->assertEquals('has static attribute "privateStaticAttribute"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('stdClass'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * @covers PHPUnit_Framework_Assert::classHasStaticAttribute + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassHasStaticAttribute2() + { + $constraint = PHPUnit_Framework_Assert::classHasStaticAttribute('foo'); + + try { + $constraint->evaluate('stdClass', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::classHasStaticAttribute + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassNotHasStaticAttribute() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::classHasStaticAttribute('privateStaticAttribute') + ); + + $this->assertTrue($constraint->evaluate('stdClass', '', TRUE)); + $this->assertFalse($constraint->evaluate('ClassWithNonPublicAttributes', '', TRUE)); + $this->assertEquals('does not have static attribute "privateStaticAttribute"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('ClassWithNonPublicAttributes'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::classHasStaticAttribute + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintClassNotHasStaticAttribute2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::classHasStaticAttribute('privateStaticAttribute') + ); + + try { + $constraint->evaluate('ClassWithNonPublicAttributes', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ObjectHasAttribute + * @covers PHPUnit_Framework_Assert::objectHasAttribute + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintObjectHasAttribute() + { + $constraint = PHPUnit_Framework_Assert::objectHasAttribute('privateAttribute'); + + $this->assertTrue($constraint->evaluate(new ClassWithNonPublicAttributes, '', TRUE)); + $this->assertFalse($constraint->evaluate(new stdClass, '', TRUE)); + $this->assertEquals('has attribute "privateAttribute"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(new stdClass); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ObjectHasAttribute + * @covers PHPUnit_Framework_Assert::objectHasAttribute + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintObjectHasAttribute2() + { + $constraint = PHPUnit_Framework_Assert::objectHasAttribute('privateAttribute'); + + try { + $constraint->evaluate(new stdClass, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ObjectHasAttribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::objectHasAttribute + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintObjectNotHasAttribute() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::objectHasAttribute('privateAttribute') + ); + + $this->assertTrue($constraint->evaluate(new stdClass, '', TRUE)); + $this->assertFalse($constraint->evaluate(new ClassWithNonPublicAttributes, '', TRUE)); + $this->assertEquals('does not have attribute "privateAttribute"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(new ClassWithNonPublicAttributes); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_ObjectHasAttribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::objectHasAttribute + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintObjectNotHasAttribute2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::objectHasAttribute('privateAttribute') + ); + + try { + $constraint->evaluate(new ClassWithNonPublicAttributes, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_PCREMatch + * @covers PHPUnit_Framework_Assert::matchesRegularExpression + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintPCREMatch() + { + $constraint = PHPUnit_Framework_Assert::matchesRegularExpression('/foo/'); + + $this->assertFalse($constraint->evaluate('barbazbar', '', TRUE)); + $this->assertTrue($constraint->evaluate('barfoobar', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/foo/"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('barbazbar'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_PCREMatch + * @covers PHPUnit_Framework_Assert::matchesRegularExpression + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintPCREMatch2() + { + $constraint = PHPUnit_Framework_Assert::matchesRegularExpression('/foo/'); + + try { + $constraint->evaluate('barbazbar', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_PCREMatch + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::matchesRegularExpression + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintPCRENotMatch() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::matchesRegularExpression('/foo/') + ); + + $this->assertTrue($constraint->evaluate('barbazbar', '', TRUE)); + $this->assertFalse($constraint->evaluate('barfoobar', '', TRUE)); + $this->assertEquals('does not match PCRE pattern "/foo/"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('barfoobar'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_PCREMatch + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::matchesRegularExpression + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintPCRENotMatch2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::matchesRegularExpression('/foo/') + ); + + try { + $constraint->evaluate('barfoobar', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals(<<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringMatches + * @covers PHPUnit_Framework_Assert::matches + * @covers PHPUnit_Framework_Constraint::count + */ + public function testConstraintStringMatches() + { + $constraint = PHPUnit_Framework_Assert::matches('*%c*'); + $this->assertFalse($constraint->evaluate('**', '', TRUE)); + $this->assertTrue($constraint->evaluate('***', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/^\*.\*$/s"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringMatches + * @covers PHPUnit_Framework_Assert::matches + * @covers PHPUnit_Framework_Constraint::count + */ + public function testConstraintStringMatches2() + { + $constraint = PHPUnit_Framework_Assert::matches('*%s*'); + $this->assertFalse($constraint->evaluate('**', '', TRUE)); + $this->assertTrue($constraint->evaluate('***', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/^\*[^\r\n]+\*$/s"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringMatches + * @covers PHPUnit_Framework_Assert::matches + * @covers PHPUnit_Framework_Constraint::count + */ + public function testConstraintStringMatches3() + { + $constraint = PHPUnit_Framework_Assert::matches('*%i*'); + $this->assertFalse($constraint->evaluate('**', '', TRUE)); + $this->assertTrue($constraint->evaluate('*0*', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/^\*[+-]?\d+\*$/s"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringMatches + * @covers PHPUnit_Framework_Assert::matches + * @covers PHPUnit_Framework_Constraint::count + */ + public function testConstraintStringMatches4() + { + $constraint = PHPUnit_Framework_Assert::matches('*%d*'); + $this->assertFalse($constraint->evaluate('**', '', TRUE)); + $this->assertTrue($constraint->evaluate('*0*', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/^\*\d+\*$/s"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringMatches + * @covers PHPUnit_Framework_Assert::matches + * @covers PHPUnit_Framework_Constraint::count + */ + public function testConstraintStringMatches5() + { + $constraint = PHPUnit_Framework_Assert::matches('*%x*'); + $this->assertFalse($constraint->evaluate('**', '', TRUE)); + $this->assertTrue($constraint->evaluate('*0f0f0f*', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/^\*[0-9a-fA-F]+\*$/s"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringMatches + * @covers PHPUnit_Framework_Assert::matches + * @covers PHPUnit_Framework_Constraint::count + */ + public function testConstraintStringMatches6() + { + $constraint = PHPUnit_Framework_Assert::matches('*%f*'); + $this->assertFalse($constraint->evaluate('**', '', TRUE)); + $this->assertTrue($constraint->evaluate('*1.0*', '', TRUE)); + $this->assertEquals('matches PCRE pattern "/^\*[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?\*$/s"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringStartsWith + * @covers PHPUnit_Framework_Assert::stringStartsWith + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringStartsWith() + { + $constraint = PHPUnit_Framework_Assert::stringStartsWith('prefix'); + + $this->assertFalse($constraint->evaluate('foo', '', TRUE)); + $this->assertTrue($constraint->evaluate('prefixfoo', '', TRUE)); + $this->assertEquals('starts with "prefix"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('foo'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringStartsWith + * @covers PHPUnit_Framework_Assert::stringStartsWith + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringStartsWith2() + { + $constraint = PHPUnit_Framework_Assert::stringStartsWith('prefix'); + + try { + $constraint->evaluate('foo', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringStartsWith + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::stringStartsWith + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringStartsNotWith() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::stringStartsWith('prefix') + ); + + $this->assertTrue($constraint->evaluate('foo', '', TRUE)); + $this->assertFalse($constraint->evaluate('prefixfoo', '', TRUE)); + $this->assertEquals('starts not with "prefix"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('prefixfoo'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringStartsWith + * @covers PHPUnit_Framework_Assert::stringStartsWith + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringStartsNotWith2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::stringStartsWith('prefix') + ); + + try { + $constraint->evaluate('prefixfoo', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringContains + * @covers PHPUnit_Framework_Assert::stringContains + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringContains() + { + $constraint = PHPUnit_Framework_Assert::stringContains('foo'); + + $this->assertFalse($constraint->evaluate('barbazbar', '', TRUE)); + $this->assertTrue($constraint->evaluate('barfoobar', '', TRUE)); + $this->assertEquals('contains "foo"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('barbazbar'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringContains + * @covers PHPUnit_Framework_Assert::stringContains + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringContains2() + { + $constraint = PHPUnit_Framework_Assert::stringContains('foo'); + + try { + $constraint->evaluate('barbazbar', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringContains + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::stringContains + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringNotContains() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::stringContains('foo') + ); + + $this->assertTrue($constraint->evaluate('barbazbar', '', TRUE)); + $this->assertFalse($constraint->evaluate('barfoobar', '', TRUE)); + $this->assertEquals('does not contain "foo"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('barfoobar'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringContains + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::stringContains + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringNotContains2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::stringContains('foo') + ); + + try { + $constraint->evaluate('barfoobar', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringEndsWith + * @covers PHPUnit_Framework_Assert::stringEndsWith + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringEndsWith() + { + $constraint = PHPUnit_Framework_Assert::stringEndsWith('suffix'); + + $this->assertFalse($constraint->evaluate('foo', '', TRUE)); + $this->assertTrue($constraint->evaluate('foosuffix', '', TRUE)); + $this->assertEquals('ends with "suffix"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('foo'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringEndsWith + * @covers PHPUnit_Framework_Assert::stringEndsWith + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringEndsWith2() + { + $constraint = PHPUnit_Framework_Assert::stringEndsWith('suffix'); + + try { + $constraint->evaluate('foo', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringEndsWith + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::stringEndsWith + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringEndsNotWith() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::stringEndsWith('suffix') + ); + + $this->assertTrue($constraint->evaluate('foo', '', TRUE)); + $this->assertFalse($constraint->evaluate('foosuffix', '', TRUE)); + $this->assertEquals('ends not with "suffix"', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate('foosuffix'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_StringEndsWith + * @covers PHPUnit_Framework_Assert::stringEndsWith + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintStringEndsNotWith2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::stringEndsWith('suffix') + ); + + try { + $constraint->evaluate('foosuffix', 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_TraversableContains + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayContains() + { + $constraint = new PHPUnit_Framework_Constraint_TraversableContains('foo'); + + $this->assertFalse($constraint->evaluate(array('bar'), '', TRUE)); + $this->assertTrue($constraint->evaluate(array('foo'), '', TRUE)); + $this->assertEquals("contains 'foo'", $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(array('bar')); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_TraversableContains + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayContains2() + { + $constraint = new PHPUnit_Framework_Constraint_TraversableContains('foo'); + + try { + $constraint->evaluate(array('bar'), 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_TraversableContains + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayNotContains() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + new PHPUnit_Framework_Constraint_TraversableContains('foo') + ); + + $this->assertTrue($constraint->evaluate(array('bar'), '', TRUE)); + $this->assertFalse($constraint->evaluate(array('foo'), '', TRUE)); + $this->assertEquals("does not contain 'foo'", $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(array('foo')); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_TraversableContains + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintArrayNotContains2() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + new PHPUnit_Framework_Constraint_TraversableContains('foo') + ); + + try { + $constraint->evaluate(array('foo'), 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_TraversableContains + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintSplObjectStorageContains() + { + $object = new StdClass; + $constraint = new PHPUnit_Framework_Constraint_TraversableContains($object); + $this->assertEquals("contains stdClass Object ()", $constraint->toString()); + + $storage = new SplObjectStorage; + $this->assertFalse($constraint->evaluate($storage, '', TRUE)); + + $storage->attach($object); + $this->assertTrue($constraint->evaluate($storage, '', TRUE)); + + try { + $constraint->evaluate(new SplObjectStorage); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_TraversableContains + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintSplObjectStorageContains2() + { + $object = new StdClass; + $constraint = new PHPUnit_Framework_Constraint_TraversableContains($object); + + try { + $constraint->evaluate(new SplObjectStorage, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::attributeEqualTo + * @covers PHPUnit_Framework_Constraint_Attribute + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testAttributeEqualTo() + { + $object = new ClassWithNonPublicAttributes; + $constraint = PHPUnit_Framework_Assert::attributeEqualTo('foo', 1); + + $this->assertTrue($constraint->evaluate($object, '', TRUE)); + $this->assertEquals('attribute "foo" is equal to 1', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + $constraint = PHPUnit_Framework_Assert::attributeEqualTo('foo', 2); + + $this->assertFalse($constraint->evaluate($object, '', TRUE)); + + try { + $constraint->evaluate($object); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::attributeEqualTo + * @covers PHPUnit_Framework_Constraint_Attribute + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testAttributeEqualTo2() + { + $object = new ClassWithNonPublicAttributes; + $constraint = PHPUnit_Framework_Assert::attributeEqualTo('foo', 2); + + try { + $constraint->evaluate($object, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::attributeEqualTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_Constraint_Attribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testAttributeNotEqualTo() + { + $object = new ClassWithNonPublicAttributes; + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::attributeEqualTo('foo', 2) + ); + + $this->assertTrue($constraint->evaluate($object, '', TRUE)); + $this->assertEquals('attribute "foo" is not equal to 2', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::attributeEqualTo('foo', 1) + ); + + $this->assertFalse($constraint->evaluate($object, '', TRUE)); + + try { + $constraint->evaluate($object); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Assert::attributeEqualTo + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_Constraint_Attribute + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testAttributeNotEqualTo2() + { + $object = new ClassWithNonPublicAttributes; + $constraint = PHPUnit_Framework_Assert::logicalNot( + PHPUnit_Framework_Assert::attributeEqualTo('foo', 1) + ); + + try { + $constraint->evaluate($object, 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEmpty + * @covers PHPUnit_Framework_Constraint::count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsEmpty() + { + $constraint = new PHPUnit_Framework_Constraint_IsEmpty; + + $this->assertFalse($constraint->evaluate(array('foo'), '', TRUE)); + $this->assertTrue($constraint->evaluate(array(), '', TRUE)); + $this->assertEquals('is empty', $constraint->toString()); + $this->assertEquals(1, count($constraint)); + + try { + $constraint->evaluate(array('foo')); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_IsEmpty + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintIsEmpty2() + { + $constraint = new PHPUnit_Framework_Constraint_IsEmpty; + + try { + $constraint->evaluate(array('foo'), 'custom message'); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_Count + */ + public function testConstraintCountWithAnArray() + { + $constraint = new PHPUnit_Framework_Constraint_Count(5); + + $this->assertTrue($constraint->evaluate(array(1,2,3,4,5), '', TRUE)); + $this->assertFalse($constraint->evaluate(array(1,2,3,4), '', TRUE)); + } + + /** + * @covers PHPUnit_Framework_Constraint_Count + */ + public function testConstraintCountWithAnIteratorWhichDoesNotImplementCountable() + { + $constraint = new PHPUnit_Framework_Constraint_Count(5); + + $this->assertTrue($constraint->evaluate(new TestIterator(array(1,2,3,4,5)), '', TRUE)); + $this->assertFalse($constraint->evaluate(new TestIterator(array(1,2,3,4)), '', TRUE)); + } + + /** + * @covers PHPUnit_Framework_Constraint_Count + */ + public function testConstraintCountWithAnObjectImplementingCountable() + { + $constraint = new PHPUnit_Framework_Constraint_Count(5); + + $this->assertTrue($constraint->evaluate(new ArrayObject(array(1,2,3,4,5)), '', TRUE)); + $this->assertFalse($constraint->evaluate(new ArrayObject(array(1,2,3,4)), '', TRUE)); + } + + /** + * @covers PHPUnit_Framework_Constraint_Count + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintCountFailing() + { + $constraint = new PHPUnit_Framework_Constraint_Count(5); + + try { + $constraint->evaluate(array(1,2)); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } + + /** + * @covers PHPUnit_Framework_Constraint_Count + * @covers PHPUnit_Framework_Constraint_Not + * @covers PHPUnit_Framework_Assert::logicalNot + * @covers PHPUnit_Framework_TestFailure::exceptionToString + */ + public function testConstraintNotCountFailing() + { + $constraint = PHPUnit_Framework_Assert::logicalNot( + new PHPUnit_Framework_Constraint_Count(2) + ); + + try { + $constraint->evaluate(array(1,2)); + } + + catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->assertEquals( + <<fail(); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/SuiteTest.php b/vendor/phpunit/phpunit/Tests/Framework/SuiteTest.php new file mode 100644 index 0000000..35f7bbf --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/SuiteTest.php @@ -0,0 +1,185 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'InheritedTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'NoTestCaseClass.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'NoTestCases.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'NotPublicTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'NotVoidTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'OneTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'OverrideTestCase.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Framework_SuiteTest extends PHPUnit_Framework_TestCase { + protected $result; + + protected function setUp() + { + $this->result = new PHPUnit_Framework_TestResult; + } + + public static function suite() + { + $suite = new PHPUnit_Framework_TestSuite; + + $suite->addTest(new Framework_SuiteTest('testAddTestSuite')); + $suite->addTest(new Framework_SuiteTest('testInheritedTests')); + $suite->addTest(new Framework_SuiteTest('testNoTestCases')); + $suite->addTest(new Framework_SuiteTest('testNoTestCaseClass')); + $suite->addTest(new Framework_SuiteTest('testNotExistingTestCase')); + $suite->addTest(new Framework_SuiteTest('testNotPublicTestCase')); + $suite->addTest(new Framework_SuiteTest('testNotVoidTestCase')); + $suite->addTest(new Framework_SuiteTest('testOneTestCase')); + $suite->addTest(new Framework_SuiteTest('testShadowedTests')); + + return $suite; + } + + public function testAddTestSuite() + { + $suite = new PHPUnit_Framework_TestSuite( + 'OneTestCase' + ); + + $suite->run($this->result); + + $this->assertEquals(1, count($this->result)); + } + + public function testInheritedTests() + { + $suite = new PHPUnit_Framework_TestSuite( + 'InheritedTestCase' + ); + + $suite->run($this->result); + + $this->assertTrue($this->result->wasSuccessful()); + $this->assertEquals(2, count($this->result)); + } + + public function testNoTestCases() + { + $suite = new PHPUnit_Framework_TestSuite( + 'NoTestCases' + ); + + $suite->run($this->result); + + $this->assertTrue(!$this->result->wasSuccessful()); + $this->assertEquals(1, $this->result->failureCount()); + $this->assertEquals(1, count($this->result)); + } + + /** + * @expectedException PHPUnit_Framework_Exception + */ + public function testNoTestCaseClass() + { + $suite = new PHPUnit_Framework_TestSuite('NoTestCaseClass'); + } + + public function testNotExistingTestCase() + { + $suite = new Framework_SuiteTest('notExistingMethod'); + + $suite->run($this->result); + + $this->assertEquals(0, $this->result->errorCount()); + $this->assertEquals(1, $this->result->failureCount()); + $this->assertEquals(1, count($this->result)); + } + + public function testNotPublicTestCase() + { + $suite = new PHPUnit_Framework_TestSuite( + 'NotPublicTestCase' + ); + + $this->assertEquals(2, count($suite)); + } + + public function testNotVoidTestCase() + { + $suite = new PHPUnit_Framework_TestSuite( + 'NotVoidTestCase' + ); + + $this->assertEquals(1, count($suite)); + } + + public function testOneTestCase() + { + $suite = new PHPUnit_Framework_TestSuite( + 'OneTestCase' + ); + + $suite->run($this->result); + + $this->assertEquals(0, $this->result->errorCount()); + $this->assertEquals(0, $this->result->failureCount()); + $this->assertEquals(1, count($this->result)); + $this->assertTrue($this->result->wasSuccessful()); + } + + public function testShadowedTests() + { + $suite = new PHPUnit_Framework_TestSuite( + 'OverrideTestCase' + ); + + $suite->run($this->result); + + $this->assertEquals(1, count($this->result)); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/TestCaseTest.php b/vendor/phpunit/phpunit/Tests/Framework/TestCaseTest.php new file mode 100644 index 0000000..6c65549 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/TestCaseTest.php @@ -0,0 +1,439 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Error.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionInAssertPostConditionsTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionInAssertPreConditionsTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionInSetUpTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionInTearDownTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionInTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Failure.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'NoArgTestCaseTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'OutputTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'RequirementsTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Singleton.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Success.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ThrowExceptionTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ThrowNoExceptionTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'WasRun.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ChangeCurrentWorkingDirectoryTest.php'; + +$GLOBALS['a'] = 'a'; +$_ENV['b'] = 'b'; +$_POST['c'] = 'c'; +$_GET['d'] = 'd'; +$_COOKIE['e'] = 'e'; +$_SERVER['f'] = 'f'; +$_FILES['g'] = 'g'; +$_REQUEST['h'] = 'h'; +$GLOBALS['i'] = 'i'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Framework_TestCaseTest extends PHPUnit_Framework_TestCase +{ + protected $backupGlobalsBlacklist = array('i', 'singleton'); + + public function testCaseToString() + { + $this->assertEquals( + 'Framework_TestCaseTest::testCaseToString', + $this->toString() + ); + } + + public function testSuccess() + { + $test = new Success; + $result = $test->run(); + + $this->assertEquals(0, $result->errorCount()); + $this->assertEquals(0, $result->failureCount()); + $this->assertEquals(1, count($result)); + } + + public function testFailure() + { + $test = new Failure; + $result = $test->run(); + + $this->assertEquals(0, $result->errorCount()); + $this->assertEquals(1, $result->failureCount()); + $this->assertEquals(1, count($result)); + } + + public function testError() + { + $test = new Error; + $result = $test->run(); + + $this->assertEquals(1, $result->errorCount()); + $this->assertEquals(0, $result->failureCount()); + $this->assertEquals(1, count($result)); + } + + public function testExceptionInSetUp() + { + $test = new ExceptionInSetUpTest('testSomething'); + $result = $test->run(); + + $this->assertTrue($test->setUp); + $this->assertFalse($test->assertPreConditions); + $this->assertFalse($test->testSomething); + $this->assertFalse($test->assertPostConditions); + $this->assertTrue($test->tearDown); + } + + public function testExceptionInAssertPreConditions() + { + $test = new ExceptionInAssertPreConditionsTest('testSomething'); + $result = $test->run(); + + $this->assertTrue($test->setUp); + $this->assertTrue($test->assertPreConditions); + $this->assertFalse($test->testSomething); + $this->assertFalse($test->assertPostConditions); + $this->assertTrue($test->tearDown); + } + + public function testExceptionInTest() + { + $test = new ExceptionInTest('testSomething'); + $result = $test->run(); + + $this->assertTrue($test->setUp); + $this->assertTrue($test->assertPreConditions); + $this->assertTrue($test->testSomething); + $this->assertFalse($test->assertPostConditions); + $this->assertTrue($test->tearDown); + } + + public function testExceptionInAssertPostConditions() + { + $test = new ExceptionInAssertPostConditionsTest('testSomething'); + $result = $test->run(); + + $this->assertTrue($test->setUp); + $this->assertTrue($test->assertPreConditions); + $this->assertTrue($test->testSomething); + $this->assertTrue($test->assertPostConditions); + $this->assertTrue($test->tearDown); + } + + public function testExceptionInTearDown() + { + $test = new ExceptionInTearDownTest('testSomething'); + $result = $test->run(); + + $this->assertTrue($test->setUp); + $this->assertTrue($test->assertPreConditions); + $this->assertTrue($test->testSomething); + $this->assertTrue($test->assertPostConditions); + $this->assertTrue($test->tearDown); + } + + public function testNoArgTestCasePasses() + { + $result = new PHPUnit_Framework_TestResult; + $t = new PHPUnit_Framework_TestSuite('NoArgTestCaseTest'); + + $t->run($result); + + $this->assertEquals(1, count($result)); + $this->assertEquals(0, $result->failureCount()); + $this->assertEquals(0, $result->errorCount()); + } + + public function testWasRun() + { + $test = new WasRun; + $test->run(); + + $this->assertTrue($test->wasRun); + } + + public function testException() + { + $test = new ThrowExceptionTestCase('test'); + $test->setExpectedException('RuntimeException'); + + $result = $test->run(); + + $this->assertEquals(1, count($result)); + $this->assertTrue($result->wasSuccessful()); + } + + public function testNoException() + { + $test = new ThrowNoExceptionTestCase('test'); + $test->setExpectedException('RuntimeException'); + + $result = $test->run(); + + $this->assertEquals(1, $result->failureCount()); + $this->assertEquals(1, count($result)); + } + + public function testWrongException() + { + $test = new ThrowExceptionTestCase('test'); + $test->setExpectedException('InvalidArgumentException'); + + $result = $test->run(); + + $this->assertEquals(1, $result->failureCount()); + $this->assertEquals(1, count($result)); + } + + /** + * @backupGlobals enabled + */ + public function testGlobalsBackupPre() + { + global $a; + global $i; + + $this->assertEquals('a', $a); + $this->assertEquals('a', $GLOBALS['a']); + $this->assertEquals('b', $_ENV['b']); + $this->assertEquals('c', $_POST['c']); + $this->assertEquals('d', $_GET['d']); + $this->assertEquals('e', $_COOKIE['e']); + $this->assertEquals('f', $_SERVER['f']); + $this->assertEquals('g', $_FILES['g']); + $this->assertEquals('h', $_REQUEST['h']); + $this->assertEquals('i', $i); + $this->assertEquals('i', $GLOBALS['i']); + + $GLOBALS['a'] = 'aa'; + $GLOBALS['foo'] = 'bar'; + $_ENV['b'] = 'bb'; + $_POST['c'] = 'cc'; + $_GET['d'] = 'dd'; + $_COOKIE['e'] = 'ee'; + $_SERVER['f'] = 'ff'; + $_FILES['g'] = 'gg'; + $_REQUEST['h'] = 'hh'; + $GLOBALS['i'] = 'ii'; + + $this->assertEquals('aa', $a); + $this->assertEquals('aa', $GLOBALS['a']); + $this->assertEquals('bar', $GLOBALS['foo']); + $this->assertEquals('bb', $_ENV['b']); + $this->assertEquals('cc', $_POST['c']); + $this->assertEquals('dd', $_GET['d']); + $this->assertEquals('ee', $_COOKIE['e']); + $this->assertEquals('ff', $_SERVER['f']); + $this->assertEquals('gg', $_FILES['g']); + $this->assertEquals('hh', $_REQUEST['h']); + $this->assertEquals('ii', $i); + $this->assertEquals('ii', $GLOBALS['i']); + } + + public function testGlobalsBackupPost() + { + global $a; + global $i; + + $this->assertEquals('a', $a); + $this->assertEquals('a', $GLOBALS['a']); + $this->assertEquals('b', $_ENV['b']); + $this->assertEquals('c', $_POST['c']); + $this->assertEquals('d', $_GET['d']); + $this->assertEquals('e', $_COOKIE['e']); + $this->assertEquals('f', $_SERVER['f']); + $this->assertEquals('g', $_FILES['g']); + $this->assertEquals('h', $_REQUEST['h']); + $this->assertEquals('ii', $i); + $this->assertEquals('ii', $GLOBALS['i']); + + $this->assertArrayNotHasKey('foo', $GLOBALS); + } + + /** + * @backupGlobals enabled + * @backupStaticAttributes enabled + */ + public function testStaticAttributesBackupPre() + { + $GLOBALS['singleton'] = Singleton::getInstance(); + } + + public function testStaticAttributesBackupPost() + { + $this->assertNotSame($GLOBALS['singleton'], Singleton::getInstance()); + } + + public function testExpectOutputStringFooActualFoo() + { + $test = new OutputTestCase('testExpectOutputStringFooActualFoo'); + $result = $test->run(); + + $this->assertEquals(1, count($result)); + $this->assertTrue($result->wasSuccessful()); + } + + public function testExpectOutputStringFooActualBar() + { + $test = new OutputTestCase('testExpectOutputStringFooActualBar'); + $result = $test->run(); + + $this->assertEquals(1, count($result)); + $this->assertFalse($result->wasSuccessful()); + } + + public function testExpectOutputRegexFooActualFoo() + { + $test = new OutputTestCase('testExpectOutputRegexFooActualFoo'); + $result = $test->run(); + + $this->assertEquals(1, count($result)); + $this->assertTrue($result->wasSuccessful()); + } + + public function testExpectOutputRegexFooActualBar() + { + $test = new OutputTestCase('testExpectOutputRegexFooActualBar'); + $result = $test->run(); + + $this->assertEquals(1, count($result)); + $this->assertFalse($result->wasSuccessful()); + } + + public function testSkipsIfRequiresHigherVersionOfPHPUnit() + { + $test = new RequirementsTest('testAlwaysSkip'); + $result = $test->run(); + + $this->assertEquals(1, $result->skippedCount()); + $this->assertEquals( + 'PHPUnit 1111111 (or later) is required.', + $test->getStatusMessage() + ); + } + + public function testSkipsIfRequiresHigherVersionOfPHP() + { + $test = new RequirementsTest('testAlwaysSkip2'); + $result = $test->run(); + + $this->assertEquals(1, $result->skippedCount()); + $this->assertEquals( + 'PHP 9999999 (or later) is required.', + $test->getStatusMessage() + ); + } + + public function testSkipsIfRequiresNonExistingFunction() + { + $test = new RequirementsTest('testNine'); + $result = $test->run(); + + $this->assertEquals(1, $result->skippedCount()); + $this->assertEquals( + 'Function testFunc is required.', + $test->getStatusMessage() + ); + } + + public function testSkipsIfRequiresNonExistingExtension() + { + $test = new RequirementsTest('testTen'); + $result = $test->run(); + + $this->assertEquals( + 'Extension testExt is required.', + $test->getStatusMessage() + ); + } + + public function testSkipsProvidesMessagesForAllSkippingReasons() + { + $test = new RequirementsTest('testAllPossibleRequirements'); + $result = $test->run(); + + $this->assertEquals( + 'PHP 99-dev (or later) is required.' . PHP_EOL . + 'PHPUnit 9-dev (or later) is required.' . PHP_EOL . + 'Function testFuncOne is required.' . PHP_EOL . + 'Function testFuncTwo is required.' . PHP_EOL . + 'Extension testExtOne is required.' . PHP_EOL . + 'Extension testExtTwo is required.', + $test->getStatusMessage() + ); + } + + public function testRequiringAnExistingFunctionDoesNotSkip() + { + $test = new RequirementsTest('testExistingFunction'); + $result = $test->run(); + $this->assertEquals(0, $result->skippedCount()); + } + + public function testRequiringAnExistingExtensionDoesNotSkip() + { + $test = new RequirementsTest('testExistingExtension'); + $result = $test->run(); + $this->assertEquals(0, $result->skippedCount()); + } + + public function testCurrentWorkingDirectoryIsRestored() + { + $expectedCwd = getcwd(); + + $test = new ChangeCurrentWorkingDirectoryTest('testSomethingThatChangesTheCwd'); + $test->run(); + + $this->assertSame($expectedCwd, getcwd()); + } + +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/TestImplementorTest.php b/vendor/phpunit/phpunit/Tests/Framework/TestImplementorTest.php new file mode 100644 index 0000000..13025ea --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/TestImplementorTest.php @@ -0,0 +1,79 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'DoubleTestCase.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Success.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Framework_TestImplementorTest extends PHPUnit_Framework_TestCase +{ + protected $test; + + public function __construct() + { + $this->test = new DoubleTestCase( + new Success + ); + } + + public function testSuccessfulRun() + { + $result = new PHPUnit_Framework_TestResult; + + $this->test->run($result); + + $this->assertEquals(count($this->test), count($result)); + $this->assertEquals(0, $result->errorCount()); + $this->assertEquals(0, $result->failureCount()); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Framework/TestListenerTest.php b/vendor/phpunit/phpunit/Tests/Framework/TestListenerTest.php new file mode 100644 index 0000000..260f738 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Framework/TestListenerTest.php @@ -0,0 +1,145 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Error.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Failure.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'Success.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Framework_TestListenerTest extends PHPUnit_Framework_TestCase implements PHPUnit_Framework_TestListener +{ + protected $endCount; + protected $errorCount; + protected $failureCount; + protected $notImplementedCount; + protected $skippedCount; + protected $result; + protected $startCount; + + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->errorCount++; + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->failureCount++; + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->notImplementedCount++; + } + + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->skippedCount++; + } + + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + public function startTest(PHPUnit_Framework_Test $test) + { + $this->startCount++; + } + + public function endTest(PHPUnit_Framework_Test $test, $time) + { + $this->endCount++; + } + + protected function setUp() + { + $this->result = new PHPUnit_Framework_TestResult; + $this->result->addListener($this); + + $this->endCount = 0; + $this->failureCount = 0; + $this->notImplementedCount = 0; + $this->skippedCount = 0; + $this->startCount = 0; + } + + public function testError() + { + $test = new Error; + $test->run($this->result); + + $this->assertEquals(1, $this->errorCount); + $this->assertEquals(1, $this->endCount); + } + + public function testFailure() + { + $test = new Failure; + $test->run($this->result); + + $this->assertEquals(1, $this->failureCount); + $this->assertEquals(1, $this->endCount); + } + + public function testStartStop() + { + $test = new Success; + $test->run($this->result); + + $this->assertEquals(1, $this->startCount); + $this->assertEquals(1, $this->endCount); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/1021.phpt b/vendor/phpunit/phpunit/Tests/Regression/1021.phpt new file mode 100644 index 0000000..b3b9bc7 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/1021.phpt @@ -0,0 +1,19 @@ +--TEST-- +#1021: Depending on a test that uses a data provider does not work +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 1 assertion) diff --git a/vendor/phpunit/phpunit/Tests/Regression/1021/Issue1021Test.php b/vendor/phpunit/phpunit/Tests/Regression/1021/Issue1021Test.php new file mode 100644 index 0000000..422d580 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/1021/Issue1021Test.php @@ -0,0 +1,23 @@ +assertTrue($data); + } + + /** + * @depends testSomething + */ + public function testSomethingElse() + { + } + + public function provider() + { + return array(array(TRUE)); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/523.phpt b/vendor/phpunit/phpunit/Tests/Regression/523.phpt new file mode 100644 index 0000000..181c945 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/523.phpt @@ -0,0 +1,19 @@ +--TEST-- +#523: assertAttributeEquals does not work with classes extending ArrayIterator +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +. + +Time: %i %s, Memory: %sMb + +OK (1 test, 1 assertion) diff --git a/vendor/phpunit/phpunit/Tests/Regression/523/Issue523Test.php b/vendor/phpunit/phpunit/Tests/Regression/523/Issue523Test.php new file mode 100644 index 0000000..80124f1 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/523/Issue523Test.php @@ -0,0 +1,13 @@ +assertAttributeEquals('foo', 'field', new Issue523()); + } +}; + +class Issue523 extends ArrayIterator +{ + protected $field = 'foo'; +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/578.phpt b/vendor/phpunit/phpunit/Tests/Regression/578.phpt new file mode 100644 index 0000000..b5df403 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/578.phpt @@ -0,0 +1,37 @@ +--TEST-- +#578: Double printing of trace line for exceptions from notices and warnings +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +EEE + +Time: %i %s, Memory: %sMb + +There were 3 errors: + +1) Issue578Test::testNoticesDoublePrintStackTrace +Invalid error type specified + +%s/Issue578Test.php:%i + +2) Issue578Test::testWarningsDoublePrintStackTrace +Invalid error type specified + +%s/Issue578Test.php:%i + +3) Issue578Test::testUnexpectedExceptionsPrintsCorrectly +Exception: Double printed exception + +%s/Issue578Test.php:%i + +FAILURES! +Tests: 3, Assertions: 0, Errors: 3. diff --git a/vendor/phpunit/phpunit/Tests/Regression/578/Issue578Test.php b/vendor/phpunit/phpunit/Tests/Regression/578/Issue578Test.php new file mode 100644 index 0000000..262d97f --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/578/Issue578Test.php @@ -0,0 +1,20 @@ +iniSet('error_reporting', E_ALL | E_NOTICE); + trigger_error('Stack Trace Test Notice', E_NOTICE); + } + + public function testWarningsDoublePrintStackTrace() + { + $this->iniSet('error_reporting', E_ALL | E_NOTICE); + trigger_error('Stack Trace Test Notice', E_WARNING); + } + + public function testUnexpectedExceptionsPrintsCorrectly() + { + throw new Exception('Double printed exception'); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/684.phpt b/vendor/phpunit/phpunit/Tests/Regression/684.phpt new file mode 100644 index 0000000..de26fcd --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/684.phpt @@ -0,0 +1,26 @@ +--TEST-- +#684: Unable to find test class when no test methods exists +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Warning +No tests found in class "Foo_Bar_Issue684Test". + + +FAILURES! +Tests: 1, Assertions: 0, Failures: 1. diff --git a/vendor/phpunit/phpunit/Tests/Regression/684/Issue684Test.php b/vendor/phpunit/phpunit/Tests/Regression/684/Issue684Test.php new file mode 100644 index 0000000..e8e5d87 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/684/Issue684Test.php @@ -0,0 +1,4 @@ + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 0 assertions) diff --git a/vendor/phpunit/phpunit/Tests/Regression/783/ChildSuite.php b/vendor/phpunit/phpunit/Tests/Regression/783/ChildSuite.php new file mode 100644 index 0000000..8bac514 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/783/ChildSuite.php @@ -0,0 +1,15 @@ +addTestSuite('OneTest'); + $suite->addTestSuite('TwoTest'); + + return $suite; + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/783/OneTest.php b/vendor/phpunit/phpunit/Tests/Regression/783/OneTest.php new file mode 100644 index 0000000..3daa91b --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/783/OneTest.php @@ -0,0 +1,10 @@ +addTest(ChildSuite::suite()); + + return $suite; + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/783/TwoTest.php b/vendor/phpunit/phpunit/Tests/Regression/783/TwoTest.php new file mode 100644 index 0000000..8fa9958 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/783/TwoTest.php @@ -0,0 +1,10 @@ + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.FFF + +Time: %i %s, Memory: %sMb + +There were 3 failures: + +1) Issue244Test::testFails +Failed asserting that '123StringCode' is equal to expected exception code 'OtherString'. + +%s:%i + +2) Issue244Test::testFailsTooIfExpectationIsANumber +Failed asserting that '123StringCode' is equal to expected exception code 123. + +%s:%i + +3) Issue244Test::testFailsTooIfExceptionCodeIsANumber +Failed asserting that 123 is equal to expected exception code '123String'. + +%s:%i + +FAILURES! +Tests: 4, Assertions: 8, Failures: 3. diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/244/Issue244Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/244/Issue244Test.php new file mode 100644 index 0000000..621c4cf --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/244/Issue244Test.php @@ -0,0 +1,55 @@ +code = '123StringCode'; + } +} + +class Issue244ExceptionIntCode extends Exception +{ + public function __construct() + { + $this->code = 123; + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/322.phpt b/vendor/phpunit/phpunit/Tests/Regression/GitHub/322.phpt new file mode 100644 index 0000000..ef826ac --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/322.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-322: group commandline option should override group/exclude setting in phpunit.xml +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +Configuration read from %s + + +Starting test 'Issue322Test::testOne'. +. + +Time: %i %s, Memory: %sMb + +OK (1 test, 0 assertions) diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/322/Issue322Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/322/Issue322Test.php new file mode 100644 index 0000000..618bcaa --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/322/Issue322Test.php @@ -0,0 +1,17 @@ + + + Test.php + + + + + one + + + \ No newline at end of file diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/433.phpt b/vendor/phpunit/phpunit/Tests/Regression/GitHub/433.phpt new file mode 100644 index 0000000..8b9df96 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/433.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-433: expectOutputString not completely working as expected +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +..F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Issue433Test::testNotMatchingOutput +Failed asserting that two strings are equal. +--- Expected ++++ Actual +@@ @@ +-'foo' ++'bar' + + +FAILURES! +Tests: 3, Assertions: 3, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/433/Issue433Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/433/Issue433Test.php new file mode 100644 index 0000000..e0a91b3 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/433/Issue433Test.php @@ -0,0 +1,21 @@ +expectOutputString('test'); + print 'test'; + } + + public function testOutputWithExpectationAfter() + { + print 'test'; + $this->expectOutputString('test'); + } + + public function testNotMatchingOutput() + { + print 'bar'; + $this->expectOutputString('foo'); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/445.phpt b/vendor/phpunit/phpunit/Tests/Regression/GitHub/445.phpt new file mode 100644 index 0000000..bffe754 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/445.phpt @@ -0,0 +1,34 @@ +--TEST-- +GH-455: expectOutputString not working in strict mode +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +..F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Issue445Test::testNotMatchingOutput +Failed asserting that two strings are equal. +--- Expected ++++ Actual +@@ @@ +-'foo' ++'bar' + + +FAILURES! +Tests: 3, Assertions: 3, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/445/Issue445Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/445/Issue445Test.php new file mode 100644 index 0000000..c309025 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/445/Issue445Test.php @@ -0,0 +1,21 @@ +expectOutputString('test'); + print 'test'; + } + + public function testOutputWithExpectationAfter() + { + print 'test'; + $this->expectOutputString('test'); + } + + public function testNotMatchingOutput() + { + print 'bar'; + $this->expectOutputString('foo'); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/503.phpt b/vendor/phpunit/phpunit/Tests/Regression/GitHub/503.phpt new file mode 100644 index 0000000..3ccff27 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/503.phpt @@ -0,0 +1,34 @@ +--TEST-- +GH-503: assertEquals() Line Ending Differences Are Obscure +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Issue503Test::testCompareDifferentLineEndings +Failed asserting that two strings are identical. +--- Expected ++++ Actual +@@ @@ + #Warning: Strings contain different line endings! + foo + +%s:%i + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/503/Issue503Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/503/Issue503Test.php new file mode 100644 index 0000000..75ca8d4 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/503/Issue503Test.php @@ -0,0 +1,11 @@ +assertSame( + "foo\n", + "foo\r\n" + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/581.phpt b/vendor/phpunit/phpunit/Tests/Regression/GitHub/581.phpt new file mode 100644 index 0000000..9e6f304 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/581.phpt @@ -0,0 +1,43 @@ +--TEST-- +GH-581: PHPUnit_Util_Type::export adds extra newlines in Windows +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Issue581Test::testExportingObjectsDoesNotBreakWindowsLineFeeds +Failed asserting that two objects are equal. +--- Expected ++++ Actual +@@ @@ + stdClass Object ( + 0 => 1 + 1 => 2 + 2 => 'Test\n' + 3 => 4 +- 4 => 5 ++ 4 => 1 + 5 => 6 + 6 => 7 + 7 => 8 + ) + +%s:%i + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/581/Issue581Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/581/Issue581Test.php new file mode 100644 index 0000000..201780c --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/581/Issue581Test.php @@ -0,0 +1,10 @@ +assertEquals( + (object)array(1,2,"Test\r\n",4,5,6,7,8), + (object)array(1,2,"Test\r\n",4,1,6,7,8) + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/74.phpt b/vendor/phpunit/phpunit/Tests/Regression/GitHub/74.phpt new file mode 100644 index 0000000..851dfb2 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/74.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-74: catchable fatal error in 3.5 +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +E + +Time: %i %s, Memory: %sMb + +There was 1 error: + +1) Issue74Test::testCreateAndThrowNewExceptionInProcessIsolation +NewException: Testing GH-74 + +%s/Tests/Regression/GitHub/74/Issue74Test.php:7 +%s + +FAILURES! +Tests: 1, Assertions: 0, Errors: 1. diff --git a/vendor/phpunit/phpunit/Tests/Regression/GitHub/74/Issue74Test.php b/vendor/phpunit/phpunit/Tests/Regression/GitHub/74/Issue74Test.php new file mode 100644 index 0000000..4d68c55 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Regression/GitHub/74/Issue74Test.php @@ -0,0 +1,9 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.0.0 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'MockRunner.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'NonStatic.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.0.0 + */ +class Runner_BaseTestRunnerTest extends PHPUnit_Framework_TestCase +{ + public function testInvokeNonStaticSuite() + { + $runner = new MockRunner; + $runner->getTest('NonStatic'); + } +} diff --git a/vendor/phpunit/phpunit/Tests/TextUI/abstract-test-class.phpt b/vendor/phpunit/phpunit/Tests/TextUI/abstract-test-class.phpt new file mode 100644 index 0000000..f911e7d --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/abstract-test-class.phpt @@ -0,0 +1,29 @@ +--TEST-- +phpunit AbstractTest ../_files/AbstractTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Warning +Cannot instantiate class "AbstractTest". + + +FAILURES! +Tests: 1, Assertions: 0, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/concrete-test-class.phpt b/vendor/phpunit/phpunit/Tests/TextUI/concrete-test-class.phpt new file mode 100644 index 0000000..a46e9c4 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/concrete-test-class.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit ConcreteTest ../_files/ConcreteTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 0 assertions) + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml-isolation.phpt new file mode 100644 index 0000000..1d34333 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml-isolation.phpt @@ -0,0 +1,49 @@ +--TEST-- +phpunit --process-isolation --log-junit php://stdout DataProviderTest ../_files/DataProviderTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +..F. + + + + + + + DataProviderTest::testAdd with data set #2 (1, 1, 3) +Failed asserting that 2 matches expected 3. + +%s:%i + + + + + + + + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) DataProviderTest::testAdd with data set #2 (1, 1, 3) +Failed asserting that 2 matches expected 3. +%s:%i + +FAILURES! +Tests: 4, Assertions: 4, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml.phpt new file mode 100644 index 0000000..ce91753 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-log-xml.phpt @@ -0,0 +1,50 @@ +--TEST-- +phpunit --log-junit php://stdout DataProviderTest ../_files/DataProviderTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +..F. + + + + + + + DataProviderTest::testAdd with data set #2 (1, 1, 3) +Failed asserting that 2 matches expected 3. + +%s:%i +%s:%i + + + + + + + + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) DataProviderTest::testAdd with data set #2 (1, 1, 3) +Failed asserting that 2 matches expected 3. +%s:%i +%s:%i + +FAILURES! +Tests: 4, Assertions: 4, Failures: 1. + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-testdox.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-testdox.phpt new file mode 100644 index 0000000..a9f6084 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dataprovider-testdox.phpt @@ -0,0 +1,19 @@ +--TEST-- +phpunit --testdox DataProviderTest ../_files/DataProviderTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +DataProvider + [ ] Add diff --git a/vendor/phpunit/phpunit/Tests/TextUI/debug.phpt b/vendor/phpunit/phpunit/Tests/TextUI/debug.phpt new file mode 100644 index 0000000..842c59d --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/debug.phpt @@ -0,0 +1,28 @@ +--TEST-- +phpunit --debug BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + + +Starting test 'BankAccountTest::testBalanceIsInitiallyZero'. +. +Starting test 'BankAccountTest::testBalanceCannotBecomeNegative'. +. +Starting test 'BankAccountTest::testBalanceCannotBecomeNegative2'. +. + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/default-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/default-isolation.phpt new file mode 100644 index 0000000..a18cbe5 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/default-isolation.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --process-isolation BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/default.phpt b/vendor/phpunit/phpunit/Tests/TextUI/default.phpt new file mode 100644 index 0000000..9e010ed --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/default.phpt @@ -0,0 +1,21 @@ +--TEST-- +phpunit BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dependencies-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dependencies-isolation.phpt new file mode 100644 index 0000000..f03170a --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dependencies-isolation.phpt @@ -0,0 +1,42 @@ +--TEST-- +phpunit --process-isolation --verbose DependencyTestSuite ../_files/DependencyTestSuite.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +...FSS + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) DependencyFailureTest::testOne + +%s:%i + +There were 2 skipped tests: + +1) DependencyFailureTest::testTwo +This test depends on "DependencyFailureTest::testOne" to pass. + +%s:%i + +2) DependencyFailureTest::testThree +This test depends on "DependencyFailureTest::testTwo" to pass. + +%s:%i + +FAILURES! +Tests: 4, Assertions: 0, Failures: 1, Skipped: 2. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dependencies.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dependencies.phpt new file mode 100644 index 0000000..39dd9be --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dependencies.phpt @@ -0,0 +1,42 @@ +--TEST-- +phpunit --verbose DependencyTestSuite ../_files/DependencyTestSuite.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +...FSS + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) DependencyFailureTest::testOne + +%s:%i +%s:%i + +There were 2 skipped tests: + +1) DependencyFailureTest::testTwo +This test depends on "DependencyFailureTest::testOne" to pass. + +%s:%i + +2) DependencyFailureTest::testThree +This test depends on "DependencyFailureTest::testTwo" to pass. + +%s:%i + +FAILURES! +Tests: 4, Assertions: 0, Failures: 1, Skipped: 2. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dependencies2-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dependencies2-isolation.phpt new file mode 100644 index 0000000..5f94657 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dependencies2-isolation.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --process-isolation StackTest ../_files/StackTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 5 assertions) + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dependencies2.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dependencies2.phpt new file mode 100644 index 0000000..92c109e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dependencies2.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit StackTest ../_files/StackTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 5 assertions) + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dependencies3-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dependencies3-isolation.phpt new file mode 100644 index 0000000..5191dc9 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dependencies3-isolation.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --process-isolation MultiDependencyTest ../_files/MultiDependencyTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 2 assertions) + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/dependencies3.phpt b/vendor/phpunit/phpunit/Tests/TextUI/dependencies3.phpt new file mode 100644 index 0000000..562b8e4 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/dependencies3.phpt @@ -0,0 +1,21 @@ +--TEST-- +phpunit MultiDependencyTest ../_files/MultiDependencyTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 2 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/empty-testcase.phpt b/vendor/phpunit/phpunit/Tests/TextUI/empty-testcase.phpt new file mode 100644 index 0000000..d8e4a64 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/empty-testcase.phpt @@ -0,0 +1,29 @@ +--TEST-- +phpunit EmptyTestCaseTest ../_files/EmptyTestCaseTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +F + +Time: %i %s, Memory: %sMb + +There was 1 failure: + +1) Warning +No tests found in class "EmptyTestCaseTest". + +%s:%i + +FAILURES! +Tests: 1, Assertions: 0, Failures: 1. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/exception-stack.phpt b/vendor/phpunit/phpunit/Tests/TextUI/exception-stack.phpt new file mode 100644 index 0000000..9f3704d --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/exception-stack.phpt @@ -0,0 +1,55 @@ +--TEST-- +phpunit ExceptionStackTest ../_files/ExceptionStack.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +EE + +Time: %i %s, Memory: %sMb + +There were 2 errors: + +1) ExceptionStackTest::testPrintingChildException +ExceptionStackTestException: Child exception +message +Failed asserting that two arrays are equal. +--- Expected ++++ Actual +@@ @@ + Array ( +- 0 => 1 ++ 0 => 2 + ) + +%s:%i + +Caused by +%s:%i + +2) ExceptionStackTest::testNestedExceptions +Exception: One + +%s:%i + +Caused by +InvalidArgumentException: Two + +%s:%i + +Caused by +Exception: Three + +%s:%i + +FAILURES! +Tests: 2, Assertions: 1, Errors: 2. + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/exclude-group-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/exclude-group-isolation.phpt new file mode 100644 index 0000000..c8ebbce --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/exclude-group-isolation.phpt @@ -0,0 +1,24 @@ +--TEST-- +phpunit --process-isolation --exclude-group balanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 2 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/exclude-group.phpt b/vendor/phpunit/phpunit/Tests/TextUI/exclude-group.phpt new file mode 100644 index 0000000..fa88b5e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/exclude-group.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --exclude-group balanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +.. + +Time: %i %s, Memory: %sMb + +OK (2 tests, 2 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/failure-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/failure-isolation.phpt new file mode 100644 index 0000000..1f78cd7 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/failure-isolation.phpt @@ -0,0 +1,144 @@ +--TEST-- +phpunit --process-isolation FailureTest ../_files/FailureTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +FFFFFFFFFFFFF + +Time: %i %s, Memory: %sMb + +There were 13 failures: + +1) FailureTest::testAssertArrayEqualsArray +message +Failed asserting that two arrays are equal. +--- Expected ++++ Actual +@@ @@ + Array ( +- 0 => 1 ++ 0 => 2 + ) + +%s:%i + +2) FailureTest::testAssertIntegerEqualsInteger +message +Failed asserting that 2 matches expected 1. + +%s:%i + +3) FailureTest::testAssertObjectEqualsObject +message +Failed asserting that two objects are equal. +--- Expected ++++ Actual +@@ @@ + stdClass Object ( +- 'foo' => 'bar' ++ 'bar' => 'foo' + ) + +%s:%i + +4) FailureTest::testAssertNullEqualsString +message +Failed asserting that 'bar' matches expected null. + +%s:%i + +5) FailureTest::testAssertStringEqualsString +message +Failed asserting that two strings are equal. +--- Expected ++++ Actual +@@ @@ +-'foo' ++'bar' + +%s:%i + +6) FailureTest::testAssertTextEqualsText +message +Failed asserting that two strings are equal. +--- Expected ++++ Actual +@@ @@ + 'foo +-bar ++baz + ' + +%s:%i + +7) FailureTest::testAssertStringMatchesFormat +message +Failed asserting that format description matches text. +--- Expected ++++ Actual +@@ @@ +-*%s* ++** + +%s:%i + +8) FailureTest::testAssertNumericEqualsNumeric +message +Failed asserting that 2 matches expected 1. + +%s:%i + +9) FailureTest::testAssertTextSameText +message +Failed asserting that two strings are identical. +--- Expected ++++ Actual +@@ @@ +-foo ++bar + +%s:%i + +10) FailureTest::testAssertObjectSameObject +message +Failed asserting that two variables reference the same object. + +%s:%i + +11) FailureTest::testAssertObjectSameNull +message +Failed asserting that null is identical to an object of class "stdClass". + +%s:%i + +12) FailureTest::testAssertFloatSameFloat +message +Failed asserting that 1.5 is identical to 1.0. + +%s:%i + +13) FailureTest::testAssertStringMatchesFormatFile +Failed asserting that format description matches text. +--- Expected ++++ Actual +@@ @@ +-FOO +- ++...BAR... + +%s:%i + +FAILURES! +Tests: 13, Assertions: 14, Failures: 13. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/failure.phpt b/vendor/phpunit/phpunit/Tests/TextUI/failure.phpt new file mode 100644 index 0000000..a341865 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/failure.phpt @@ -0,0 +1,144 @@ +--TEST-- +phpunit FailureTest ../_files/FailureTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +FFFFFFFFFFFFF + +Time: %i %s, Memory: %sMb + +There were 13 failures: + +1) FailureTest::testAssertArrayEqualsArray +message +Failed asserting that two arrays are equal. +--- Expected ++++ Actual +@@ @@ + Array ( +- 0 => 1 ++ 0 => 2 + ) + +%s:%i + +2) FailureTest::testAssertIntegerEqualsInteger +message +Failed asserting that 2 matches expected 1. + +%s:%i + +3) FailureTest::testAssertObjectEqualsObject +message +Failed asserting that two objects are equal. +--- Expected ++++ Actual +@@ @@ + stdClass Object ( +- 'foo' => 'bar' ++ 'bar' => 'foo' + ) + +%s:%i + +4) FailureTest::testAssertNullEqualsString +message +Failed asserting that 'bar' matches expected null. + +%s:%i + +5) FailureTest::testAssertStringEqualsString +message +Failed asserting that two strings are equal. +--- Expected ++++ Actual +@@ @@ +-'foo' ++'bar' + +%s:%i + +6) FailureTest::testAssertTextEqualsText +message +Failed asserting that two strings are equal. +--- Expected ++++ Actual +@@ @@ + 'foo +-bar ++baz + ' + +%s:%i + +7) FailureTest::testAssertStringMatchesFormat +message +Failed asserting that format description matches text. +--- Expected ++++ Actual +@@ @@ +-*%s* ++** + +%s:%i + +8) FailureTest::testAssertNumericEqualsNumeric +message +Failed asserting that 2 matches expected 1. + +%s:%i + +9) FailureTest::testAssertTextSameText +message +Failed asserting that two strings are identical. +--- Expected ++++ Actual +@@ @@ +-foo ++bar + +%s:%i + +10) FailureTest::testAssertObjectSameObject +message +Failed asserting that two variables reference the same object. + +%s:%i + +11) FailureTest::testAssertObjectSameNull +message +Failed asserting that null is identical to an object of class "stdClass". + +%s:%i + +12) FailureTest::testAssertFloatSameFloat +message +Failed asserting that 1.5 is identical to 1.0. + +%s:%i + +13) FailureTest::testAssertStringMatchesFormatFile +Failed asserting that format description matches text. +--- Expected ++++ Actual +@@ @@ +-FOO +- ++...BAR... + +%s:%i + +FAILURES! +Tests: 13, Assertions: 14, Failures: 13. + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/fatal-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/fatal-isolation.phpt new file mode 100644 index 0000000..bc8cf1d --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/fatal-isolation.phpt @@ -0,0 +1,35 @@ +--TEST-- +phpunit FatalTest ../_files/FatalTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +E + +Time: %i %s, Memory: %sMb + +There was 1 error: + +1) FatalTest::testFatalError +PHPUnit_Framework_Exception: Fatal error: Call to undefined function non_existing_function() in %s + +%s:%i + +Caused by +ErrorException: unserialize(): Error at offset %i of %i bytes + +%s:%i + +FAILURES! +Tests: 1, Assertions: 0, Errors: 1. + diff --git a/vendor/phpunit/phpunit/Tests/TextUI/fatal.phpt b/vendor/phpunit/phpunit/Tests/TextUI/fatal.phpt new file mode 100644 index 0000000..da19557 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/fatal.phpt @@ -0,0 +1,17 @@ +--TEST-- +phpunit FatalTest ../_files/FatalTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + + +Fatal error: Call to undefined function non_existing_function() in %s diff --git a/vendor/phpunit/phpunit/Tests/TextUI/filter-class-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/filter-class-isolation.phpt new file mode 100644 index 0000000..85fff4e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/filter-class-isolation.phpt @@ -0,0 +1,24 @@ +--TEST-- +phpunit --process-isolation --filter BankAccountTest BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/filter-class.phpt b/vendor/phpunit/phpunit/Tests/TextUI/filter-class.phpt new file mode 100644 index 0000000..459cf28 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/filter-class.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --filter BankAccountTest BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/filter-method-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/filter-method-isolation.phpt new file mode 100644 index 0000000..aede555 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/filter-method-isolation.phpt @@ -0,0 +1,24 @@ +--TEST-- +phpunit --process-isolation --filter testBalanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +. + +Time: %i %s, Memory: %sMb + +OK (1 test, 1 assertion) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/filter-method.phpt b/vendor/phpunit/phpunit/Tests/TextUI/filter-method.phpt new file mode 100644 index 0000000..e553a03 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/filter-method.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --filter testBalanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +. + +Time: %i %s, Memory: %sMb + +OK (1 test, 1 assertion) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/filter-no-results.phpt b/vendor/phpunit/phpunit/Tests/TextUI/filter-no-results.phpt new file mode 100644 index 0000000..03f6995 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/filter-no-results.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --filter testBalanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + + + +Time: %i %s, Memory: %sMb + +No tests executed! diff --git a/vendor/phpunit/phpunit/Tests/TextUI/group-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/group-isolation.phpt new file mode 100644 index 0000000..5abbada --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/group-isolation.phpt @@ -0,0 +1,24 @@ +--TEST-- +phpunit --process-isolation --group balanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +. + +Time: %i %s, Memory: %sMb + +OK (1 test, 1 assertion) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/group.phpt b/vendor/phpunit/phpunit/Tests/TextUI/group.phpt new file mode 100644 index 0000000..77003f2 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/group.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --group balanceIsInitiallyZero BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +. + +Time: %i %s, Memory: %sMb + +OK (1 test, 1 assertion) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/help.phpt b/vendor/phpunit/phpunit/Tests/TextUI/help.phpt new file mode 100644 index 0000000..7a5ce43 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/help.phpt @@ -0,0 +1,67 @@ +--TEST-- +phpunit +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +Usage: phpunit [switches] UnitTest [UnitTest.php] + phpunit [switches] + + --log-junit Log test execution in JUnit XML format to file. + --log-tap Log test execution in TAP format to file. + --log-json Log test execution in JSON format. + + --coverage-clover Generate code coverage report in Clover XML format. + --coverage-html Generate code coverage report in HTML format. + --coverage-php Serialize PHP_CodeCoverage object to file. + --coverage-text= Generate code coverage report in text format. + Default to writing to the standard output. + + --testdox-html Write agile documentation in HTML format to file. + --testdox-text Write agile documentation in Text format to file. + + --filter Filter which tests to run. + --testsuite Filter which testsuite to run. + --group ... Only runs tests from the specified group(s). + --exclude-group ... Exclude tests from the specified group(s). + --list-groups List available test groups. + --test-suffix ... Only search for test in files with specified + suffix(es). Default: Test.php,.phpt + + --loader TestSuiteLoader implementation to use. + --printer TestSuiteListener implementation to use. + --repeat Runs the test(s) repeatedly. + + --tap Report test execution progress in TAP format. + --testdox Report test execution progress in TestDox format. + + --colors Use colors in output. + --stderr Write to STDERR instead of STDOUT. + --stop-on-error Stop execution upon first error. + --stop-on-failure Stop execution upon first error or failure. + --stop-on-skipped Stop execution upon first skipped test. + --stop-on-incomplete Stop execution upon first incomplete test. + --strict Run tests in strict mode. + -v|--verbose Output more verbose information. + --debug Display debugging information during test execution. + + --process-isolation Run each test in a separate PHP process. + --no-globals-backup Do not backup and restore $GLOBALS for each test. + --static-backup Backup and restore static attributes for each test. + + --bootstrap A "bootstrap" PHP file that is run before the tests. + -c|--configuration Read configuration from XML file. + --no-configuration Ignore default configuration file (phpunit.xml). + --include-path Prepend PHP's include_path with given path(s). + -d key[=value] Sets a php.ini value. + + -h|--help Prints this usage information. + --version Prints the version and exits. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/help2.phpt b/vendor/phpunit/phpunit/Tests/TextUI/help2.phpt new file mode 100644 index 0000000..9f3d309 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/help2.phpt @@ -0,0 +1,68 @@ +--TEST-- +phpunit --help +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +Usage: phpunit [switches] UnitTest [UnitTest.php] + phpunit [switches] + + --log-junit Log test execution in JUnit XML format to file. + --log-tap Log test execution in TAP format to file. + --log-json Log test execution in JSON format. + + --coverage-clover Generate code coverage report in Clover XML format. + --coverage-html Generate code coverage report in HTML format. + --coverage-php Serialize PHP_CodeCoverage object to file. + --coverage-text= Generate code coverage report in text format. + Default to writing to the standard output. + + --testdox-html Write agile documentation in HTML format to file. + --testdox-text Write agile documentation in Text format to file. + + --filter Filter which tests to run. + --testsuite Filter which testsuite to run. + --group ... Only runs tests from the specified group(s). + --exclude-group ... Exclude tests from the specified group(s). + --list-groups List available test groups. + --test-suffix ... Only search for test in files with specified + suffix(es). Default: Test.php,.phpt + + --loader TestSuiteLoader implementation to use. + --printer TestSuiteListener implementation to use. + --repeat Runs the test(s) repeatedly. + + --tap Report test execution progress in TAP format. + --testdox Report test execution progress in TestDox format. + + --colors Use colors in output. + --stderr Write to STDERR instead of STDOUT. + --stop-on-error Stop execution upon first error. + --stop-on-failure Stop execution upon first error or failure. + --stop-on-skipped Stop execution upon first skipped test. + --stop-on-incomplete Stop execution upon first incomplete test. + --strict Run tests in strict mode. + -v|--verbose Output more verbose information. + --debug Display debugging information during test execution. + + --process-isolation Run each test in a separate PHP process. + --no-globals-backup Do not backup and restore $GLOBALS for each test. + --static-backup Backup and restore static attributes for each test. + + --bootstrap A "bootstrap" PHP file that is run before the tests. + -c|--configuration Read configuration from XML file. + --no-configuration Ignore default configuration file (phpunit.xml). + --include-path Prepend PHP's include_path with given path(s). + -d key[=value] Sets a php.ini value. + + -h|--help Prints this usage information. + --version Prints the version and exits. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/list-groups.phpt b/vendor/phpunit/phpunit/Tests/TextUI/list-groups.phpt new file mode 100644 index 0000000..5b07ed0 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/list-groups.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --list-groups BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +Available test group(s): + - Sebastian Bergmann + - balanceCannotBecomeNegative + - balanceIsInitiallyZero + - specification diff --git a/vendor/phpunit/phpunit/Tests/TextUI/log-json.phpt b/vendor/phpunit/phpunit/Tests/TextUI/log-json.phpt new file mode 100644 index 0000000..dcc348e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/log-json.phpt @@ -0,0 +1,77 @@ +--TEST-- +phpunit --log-json php://stdout BankAccountTest ../_files/BankAccountTest.php +--SKIPIF-- += 5.4"; +} +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +{ + "event": "suiteStart", + "suite": "BankAccountTest", + "tests": 3 +}{ + "event": "testStart", + "suite": "BankAccountTest", + "test": "BankAccountTest::testBalanceIsInitiallyZero" +}.{ + "event": "test", + "suite": "BankAccountTest", + "test": "BankAccountTest::testBalanceIsInitiallyZero", + "status": "pass", + "time": %f, + "trace": [ + + ], + "message": "", + "output": "" +}{ + "event": "testStart", + "suite": "BankAccountTest", + "test": "BankAccountTest::testBalanceCannotBecomeNegative" +}.{ + "event": "test", + "suite": "BankAccountTest", + "test": "BankAccountTest::testBalanceCannotBecomeNegative", + "status": "pass", + "time": %f, + "trace": [ + + ], + "message": "", + "output": "" +}{ + "event": "testStart", + "suite": "BankAccountTest", + "test": "BankAccountTest::testBalanceCannotBecomeNegative2" +}.{ + "event": "test", + "suite": "BankAccountTest", + "test": "BankAccountTest::testBalanceCannotBecomeNegative2", + "status": "pass", + "time": %f, + "trace": [ + + ], + "message": "", + "output": "" +} + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/log-tap.phpt b/vendor/phpunit/phpunit/Tests/TextUI/log-tap.phpt new file mode 100644 index 0000000..1d965a2 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/log-tap.phpt @@ -0,0 +1,28 @@ +--TEST-- +phpunit --log-tap php://stdout BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +TAP version 13 +.ok 1 - BankAccountTest::testBalanceIsInitiallyZero +.ok 2 - BankAccountTest::testBalanceCannotBecomeNegative +.ok 3 - BankAccountTest::testBalanceCannotBecomeNegative2 +1..3 + + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/log-xml.phpt b/vendor/phpunit/phpunit/Tests/TextUI/log-xml.phpt new file mode 100644 index 0000000..2c95fa6 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/log-xml.phpt @@ -0,0 +1,31 @@ +--TEST-- +phpunit --log-junit php://stdout BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + + + + + + + + + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/strict-incomplete.phpt b/vendor/phpunit/phpunit/Tests/TextUI/strict-incomplete.phpt new file mode 100644 index 0000000..5c41497 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/strict-incomplete.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --strict IncompleteTest ../_files/IncompleteTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +I + +Time: %i %s, Memory: %sMb + +OK, but incomplete or skipped tests! +Tests: 1, Assertions: 0, Incomplete: 1. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/strict-isolation.phpt b/vendor/phpunit/phpunit/Tests/TextUI/strict-isolation.phpt new file mode 100644 index 0000000..ef0ae37 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/strict-isolation.phpt @@ -0,0 +1,24 @@ +--TEST-- +phpunit --strict --process-isolation IncompleteTest ../_files/IncompleteTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +I + +Time: %i %s, Memory: %sMb + +OK, but incomplete or skipped tests! +Tests: 1, Assertions: 0, Incomplete: 1. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/strict.phpt b/vendor/phpunit/phpunit/Tests/TextUI/strict.phpt new file mode 100644 index 0000000..f566ef6 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/strict.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --strict NothingTest ../_files/NothingTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +I + +Time: %i %s, Memory: %sMb + +OK, but incomplete or skipped tests! +Tests: 1, Assertions: 0, Incomplete: 1. diff --git a/vendor/phpunit/phpunit/Tests/TextUI/tap.phpt b/vendor/phpunit/phpunit/Tests/TextUI/tap.phpt new file mode 100644 index 0000000..f537080 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/tap.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --tap BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +TAP version 13 +ok 1 - BankAccountTest::testBalanceIsInitiallyZero +ok 2 - BankAccountTest::testBalanceCannotBecomeNegative +ok 3 - BankAccountTest::testBalanceCannotBecomeNegative2 +1..3 diff --git a/vendor/phpunit/phpunit/Tests/TextUI/test-suffix-multiple.phpt b/vendor/phpunit/phpunit/Tests/TextUI/test-suffix-multiple.phpt new file mode 100644 index 0000000..145ebeb --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/test-suffix-multiple.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --test-suffix .test.php,.my.php ../_files/ +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +..... + +Time: %i %s, Memory: %sMb + +OK (5 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/test-suffix-single.phpt b/vendor/phpunit/phpunit/Tests/TextUI/test-suffix-single.phpt new file mode 100644 index 0000000..4d63efa --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/test-suffix-single.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --test-suffix .test.php ../_files/ +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +... + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/testdox-html.phpt b/vendor/phpunit/phpunit/Tests/TextUI/testdox-html.phpt new file mode 100644 index 0000000..61de71d --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/testdox-html.phpt @@ -0,0 +1,23 @@ +--TEST-- +phpunit --testdox-html php://stdout BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +

      BankAccount

        ...
      • Balance is initially zero
      • Balance cannot become negative
      + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/testdox-text.phpt b/vendor/phpunit/phpunit/Tests/TextUI/testdox-text.phpt new file mode 100644 index 0000000..8a57c81 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/testdox-text.phpt @@ -0,0 +1,27 @@ +--TEST-- +phpunit --testdox-text php://stdout BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +BankAccount +... [x] Balance is initially zero + [x] Balance cannot become negative + + + +Time: %i %s, Memory: %sMb + +OK (3 tests, 3 assertions) diff --git a/vendor/phpunit/phpunit/Tests/TextUI/testdox.phpt b/vendor/phpunit/phpunit/Tests/TextUI/testdox.phpt new file mode 100644 index 0000000..0d24c0e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/TextUI/testdox.phpt @@ -0,0 +1,21 @@ +--TEST-- +phpunit --testdox php://stdout BankAccountTest ../_files/BankAccountTest.php +--FILE-- + +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann. + +BankAccount + [x] Balance is initially zero + [x] Balance cannot become negative + diff --git a/vendor/phpunit/phpunit/Tests/Util/ClassTest.php b/vendor/phpunit/phpunit/Tests/Util/ClassTest.php new file mode 100644 index 0000000..1086717 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/ClassTest.php @@ -0,0 +1,84 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @author Ben Selby + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.5.6 + */ + +require_once 'PHPUnit/Framework/TestCase.php'; + +require_once 'PHPUnit/Util/Class.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @author Ben Selby + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.5.6 + */ +class Util_ClassTest extends PHPUnit_Framework_TestCase +{ + /** + * Test that if a dynamic variable is defined on a class then + * the $attribute variable will be NULL, but the variable defined + * will be a public one so we are safe to return it + * + * Currently $attribute is NULL but we try and call isPublic() on it. + * This breaks for php 5.2.10 + * + * @covers PHPUnit_Util_Class::getObjectAttribute + * + * @return void + */ + public function testGetObjectAttributeCanHandleDynamicVariables() + { + $attributeName = '_variable'; + $object = new stdClass(); + $object->$attributeName = 'Test'; + + $actual = PHPUnit_Util_Class::getObjectAttribute($object, $attributeName); + $this->assertEquals('Test', $actual); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Util/ConfigurationTest.php b/vendor/phpunit/phpunit/Tests/Util/ConfigurationTest.php new file mode 100644 index 0000000..cea57eb --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/ConfigurationTest.php @@ -0,0 +1,390 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class Util_ConfigurationTest extends PHPUnit_Framework_TestCase +{ + protected $configuration; + + protected function setUp() + { + $this->configuration = PHPUnit_Util_Configuration::getInstance( + dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'configuration.xml' + ); + } + + /** + * @expectedException PHPUnit_Framework_Exception + */ + public function testExceptionIsThrownForNotExistingConfigurationFile() + { + PHPUnit_Util_Configuration::getInstance('not_existing_file.xml'); + } + + public function testFilterConfigurationIsReadCorrectly() + { + $this->assertEquals( + array( + 'blacklist' => + array( + 'include' => + array( + 'directory' => + array( + 0 => + array( + 'path' => '/path/to/files', + 'prefix' => '', + 'suffix' => '.php', + 'group' => 'DEFAULT' + ), + ), + 'file' => + array( + 0 => '/path/to/file', + ), + ), + 'exclude' => + array( + 'directory' => + array( + 0 => + array( + 'path' => '/path/to/files', + 'prefix' => '', + 'suffix' => '.php', + 'group' => 'DEFAULT' + ), + ), + 'file' => + array( + 0 => '/path/to/file', + ), + ), + ), + 'whitelist' => + array( + 'addUncoveredFilesFromWhitelist' => TRUE, + 'processUncoveredFilesFromWhitelist' => FALSE, + 'include' => + array( + 'directory' => + array( + 0 => + array( + 'path' => '/path/to/files', + 'prefix' => '', + 'suffix' => '.php', + 'group' => 'DEFAULT' + ), + ), + 'file' => + array( + 0 => '/path/to/file', + ), + ), + 'exclude' => + array( + 'directory' => + array( + 0 => + array( + 'path' => '/path/to/files', + 'prefix' => '', + 'suffix' => '.php', + 'group' => 'DEFAULT' + ), + ), + 'file' => + array( + 0 => '/path/to/file', + ), + ), + ), + ), + $this->configuration->getFilterConfiguration() + ); + } + + public function testGroupConfigurationIsReadCorrectly() + { + $this->assertEquals( + array( + 'include' => + array( + 0 => 'name', + ), + 'exclude' => + array( + 0 => 'name', + ), + ), + $this->configuration->getGroupConfiguration() + ); + } + + public function testListenerConfigurationIsReadCorrectly() + { + $dir = __DIR__; + $includePath = ini_get('include_path'); + + ini_set('include_path', $dir . PATH_SEPARATOR . $includePath); + + $this->assertEquals( + array( + 0 => + array( + 'class' => 'MyListener', + 'file' => '/optional/path/to/MyListener.php', + 'arguments' => + array( + 0 => + array( + 0 => 'Sebastian', + ), + 1 => 22, + 2 => 'April', + 3 => 19.78, + 4 => NULL, + 5 => new stdClass, + 6 => dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'MyTestFile.php', + 7 => dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'MyRelativePath', + ), + ), + array( + 'class' => 'IncludePathListener', + 'file' => __FILE__, + 'arguments' => array() + ), + array( + 'class' => 'CompactArgumentsListener', + 'file' => '/CompactArgumentsListener.php', + 'arguments' => + array( + 0 => 42 + ), + ), + ), + $this->configuration->getListenerConfiguration() + ); + + ini_set('include_path', $includePath); + } + + public function testLoggingConfigurationIsReadCorrectly() + { + $this->assertEquals( + array( + 'charset' => 'UTF-8', + 'lowUpperBound' => '35', + 'highLowerBound' => '70', + 'highlight' => FALSE, + 'coverage-html' => '/tmp/report', + 'coverage-clover' => '/tmp/clover.xml', + 'json' => '/tmp/logfile.json', + 'plain' => '/tmp/logfile.txt', + 'tap' => '/tmp/logfile.tap', + 'logIncompleteSkipped' => FALSE, + 'junit' => '/tmp/logfile.xml', + 'testdox-html' => '/tmp/testdox.html', + 'testdox-text' => '/tmp/testdox.txt', + ), + $this->configuration->getLoggingConfiguration() + ); + } + + public function testPHPConfigurationIsReadCorrectly() + { + $this->assertEquals( + array( + 'include_path' => + array( + dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . '.', + '/path/to/lib' + ), + 'ini'=> array('foo' => 'bar'), + 'const'=> array('FOO' => FALSE, 'BAR' => TRUE), + 'var'=> array('foo' => FALSE), + 'env'=> array('foo' => TRUE), + 'post'=> array('foo' => 'bar'), + 'get'=> array('foo' => 'bar'), + 'cookie'=> array('foo' => 'bar'), + 'server'=> array('foo' => 'bar'), + 'files'=> array('foo' => 'bar'), + 'request'=> array('foo' => 'bar'), + ), + $this->configuration->getPHPConfiguration() + ); + } + + /** + * @backupGlobals enabled + */ + public function testPHPConfigurationIsHandledCorrectly() + { + $this->configuration->handlePHPConfiguration(); + + $path = dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . '.' . PATH_SEPARATOR . '/path/to/lib'; + $this->assertStringStartsWith($path, ini_get('include_path')); + $this->assertEquals(FALSE, FOO); + $this->assertEquals(TRUE, BAR); + $this->assertEquals(FALSE, $GLOBALS['foo']); + $this->assertEquals(TRUE, $_ENV['foo']); + $this->assertEquals(TRUE, getenv('foo')); + $this->assertEquals('bar', $_POST['foo']); + $this->assertEquals('bar', $_GET['foo']); + $this->assertEquals('bar', $_COOKIE['foo']); + $this->assertEquals('bar', $_SERVER['foo']); + $this->assertEquals('bar', $_FILES['foo']); + $this->assertEquals('bar', $_REQUEST['foo']); + } + + public function testPHPUnitConfigurationIsReadCorrectly() + { + $this->assertEquals( + array( + 'backupGlobals' => TRUE, + 'backupStaticAttributes' => FALSE, + 'bootstrap' => '/path/to/bootstrap.php', + 'cacheTokens' => FALSE, + 'colors' => FALSE, + 'convertErrorsToExceptions' => TRUE, + 'convertNoticesToExceptions' => TRUE, + 'convertWarningsToExceptions' => TRUE, + 'forceCoversAnnotation' => FALSE, + 'mapTestClassNameToCoveredClassName' => FALSE, + 'printerClass' => 'PHPUnit_TextUI_ResultPrinter', + 'stopOnFailure' => FALSE, + 'strict' => FALSE, + 'testSuiteLoaderClass' => 'PHPUnit_Runner_StandardTestSuiteLoader', + 'verbose' => FALSE, + 'timeoutForSmallTests' => 1, + 'timeoutForMediumTests' => 10, + 'timeoutForLargeTests' => 60 + ), + $this->configuration->getPHPUnitConfiguration() + ); + } + + public function testSeleniumBrowserConfigurationIsReadCorrectly() + { + $this->assertEquals( + array( + 0 => + array( + 'name' => 'Firefox on Linux', + 'browser' => '*firefox /usr/lib/firefox/firefox-bin', + 'host' => 'my.linux.box', + 'port' => 4444, + 'timeout' => 30000, + ), + ), + $this->configuration->getSeleniumBrowserConfiguration() + ); + } + + public function testXincludeInConfiguration() + { + $configurationWithXinclude = PHPUnit_Util_Configuration::getInstance( + dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'configuration_xinclude.xml' + ); + + $this->assertConfigurationEquals( + $this->configuration, + $configurationWithXinclude + ); + } + + /** + * Asserts that the values in $actualConfiguration equal $expectedConfiguration. + * + * @param PHPUnit_Util_Configuration $expectedConfiguration + * @param PHPUnit_Util_Configuration $actualConfiguration + * @return void + */ + protected function assertConfigurationEquals( PHPUnit_Util_Configuration $expectedConfiguration, PHPUnit_Util_Configuration $actualConfiguration ) + { + $this->assertEquals( + $expectedConfiguration->getFilterConfiguration(), + $actualConfiguration->getFilterConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getGroupConfiguration(), + $actualConfiguration->getGroupConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getListenerConfiguration(), + $actualConfiguration->getListenerConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getLoggingConfiguration(), + $actualConfiguration->getLoggingConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getPHPConfiguration(), + $actualConfiguration->getPHPConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getPHPUnitConfiguration(), + $actualConfiguration->getPHPUnitConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getSeleniumBrowserConfiguration(), + $actualConfiguration->getSeleniumBrowserConfiguration() + ); + $this->assertEquals( + $expectedConfiguration->getTestSuiteConfiguration(), + $actualConfiguration->getTestSuiteConfiguration() + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Util/DiffTest.php b/vendor/phpunit/phpunit/Tests/Util/DiffTest.php new file mode 100644 index 0000000..3f67e6d --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/DiffTest.php @@ -0,0 +1,268 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class Util_DiffTest extends PHPUnit_Framework_TestCase +{ + const REMOVED = 2; + const ADDED = 1; + const OLD = 0; + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorMessage() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-a\n+b\n", + PHPUnit_Util_Diff::diff('a', 'b') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorMessage_toArray() + { + $diff = array(); + $diff[] = array('a', self::REMOVED); + $diff[] = array('b', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('a', 'b') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorStartSame() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-ba\n+bc\n", + PHPUnit_Util_Diff::diff('ba', 'bc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorStartSame_toArray() + { + $diff = array(); + $diff[] = array('ba', self::REMOVED); + $diff[] = array('bc', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('ba', 'bc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorEndSame() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-ab\n+cb\n", + PHPUnit_Util_Diff::diff('ab', 'cb') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorEndSame_toArray() + { + $diff = array(); + $diff[] = array('ab', self::REMOVED); + $diff[] = array('cb', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('ab', 'cb') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorStartAndEndSame() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-abc\n+adc\n", + PHPUnit_Util_Diff::diff('abc', 'adc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorStartAndEndSame_toArray() + { + $diff = array(); + $diff[] = array('abc', self::REMOVED); + $diff[] = array('adc', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('abc', 'adc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorStartSameComplete() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-ab\n+abc\n", + PHPUnit_Util_Diff::diff('ab', 'abc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorStartSameComplete_toArray() + { + $diff = array(); + $diff[] = array('ab', self::REMOVED); + $diff[] = array('abc', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('ab', 'abc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorEndSameComplete() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-bc\n+abc\n", + PHPUnit_Util_Diff::diff('bc', 'abc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorEndSameComplete_toArray() + { + $diff = array(); + $diff[] = array('bc', self::REMOVED); + $diff[] = array('abc', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('bc', 'abc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorOverlapingMatches() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-abc\n+abbc\n", + PHPUnit_Util_Diff::diff('abc', 'abbc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorOverlapingMatches_toArray() + { + $diff = array(); + $diff[] = array('abc', self::REMOVED); + $diff[] = array('abbc', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('abc', 'abbc') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diff + */ + public function testComparisonErrorOverlapingMatches2() + { + $this->assertEquals( + "--- Expected\n+++ Actual\n@@ @@\n-abcdde\n+abcde\n", + PHPUnit_Util_Diff::diff('abcdde', 'abcde') + ); + } + + /** + * @covers PHPUnit_Util_Diff::diffToArray + */ + public function testComparisonErrorOverlapingMatches2_toArray() + { + $diff = array(); + $diff[] = array('abcdde', self::REMOVED); + $diff[] = array('abcde', self::ADDED); + + $this->assertEquals( + $diff, + PHPUnit_Util_Diff::diffToArray('abcdde', 'abcde') + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Util/TestDox/NamePrettifierTest.php b/vendor/phpunit/phpunit/Tests/Util/TestDox/NamePrettifierTest.php new file mode 100644 index 0000000..1adeb79 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/TestDox/NamePrettifierTest.php @@ -0,0 +1,108 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.1.0 + */ +class Util_TestDox_NamePrettifierTest extends PHPUnit_Framework_TestCase +{ + protected $namePrettifier; + + protected function setUp() + { + $this->namePrettifier = new PHPUnit_Util_TestDox_NamePrettifier; + } + + public function testTitleHasSensibleDefaults() + { + $this->assertEquals('Foo', $this->namePrettifier->prettifyTestClass('FooTest')); + $this->assertEquals('Foo', $this->namePrettifier->prettifyTestClass('TestFoo')); + $this->assertEquals('Foo', $this->namePrettifier->prettifyTestClass('TestFooTest')); + } + + public function testCaterForUserDefinedSuffix() + { + $this->namePrettifier->setSuffix('TestCase'); + $this->namePrettifier->setPrefix(NULL); + + $this->assertEquals('Foo', $this->namePrettifier->prettifyTestClass('FooTestCase')); + $this->assertEquals('TestFoo', $this->namePrettifier->prettifyTestClass('TestFoo')); + $this->assertEquals('FooTest', $this->namePrettifier->prettifyTestClass('FooTest')); + } + + public function testCaterForUserDefinedPrefix() + { + $this->namePrettifier->setSuffix(NULL); + $this->namePrettifier->setPrefix('XXX'); + + $this->assertEquals('Foo', $this->namePrettifier->prettifyTestClass('XXXFoo')); + $this->assertEquals('TestXXX', $this->namePrettifier->prettifyTestClass('TestXXX')); + $this->assertEquals('XXX', $this->namePrettifier->prettifyTestClass('XXXXXX')); + } + + public function testTestNameIsConvertedToASentence() + { + $this->assertEquals('This is a test', $this->namePrettifier->prettifyTestMethod('testThisIsATest')); + $this->assertEquals('This is a test', $this->namePrettifier->prettifyTestMethod('testThisIsATest2')); + $this->assertEquals('this is a test', $this->namePrettifier->prettifyTestMethod('this_is_a_test')); + $this->assertEquals('Foo for bar is 0', $this->namePrettifier->prettifyTestMethod('testFooForBarIs0')); + $this->assertEquals('Foo for baz is 1', $this->namePrettifier->prettifyTestMethod('testFooForBazIs1')); + } + + /** + * @ticket 224 + */ + public function testTestNameIsNotGroupedWhenNotInSequence() + { + $this->assertEquals('Sets redirect header on 301', $this->namePrettifier->prettifyTestMethod('testSetsRedirectHeaderOn301')); + $this->assertEquals('Sets redirect header on 302', $this->namePrettifier->prettifyTestMethod('testSetsRedirectHeaderOn302')); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Util/TestTest.php b/vendor/phpunit/phpunit/Tests/Util/TestTest.php new file mode 100644 index 0000000..aa0553e --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/TestTest.php @@ -0,0 +1,236 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.6 + */ + +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ExceptionNamespaceTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'RequirementsTest.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'RequirementsClassDocBlockTest.php'; + +/** + * + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.6 + */ +class Util_TestTest extends PHPUnit_Framework_TestCase +{ + public function testGetExpectedException() + { + $this->assertSame( + array('class' => 'FooBarBaz', 'code' => NULL, 'message' => ''), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testOne') + ); + + $this->assertSame( + array('class' => 'Foo_Bar_Baz', 'code' => NULL, 'message' => ''), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testTwo') + ); + + $this->assertSame( + array('class' => 'Foo\Bar\Baz', 'code' => NULL, 'message' => ''), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testThree') + ); + + $this->assertSame( + array('class' => 'ほげ', 'code' => NULL, 'message' => ''), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testFour') + ); + + $this->assertSame( + array('class' => 'Class', 'code' => 1234, 'message' => 'Message'), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testFive') + ); + + $this->assertSame( + array('class' => 'Class', 'code' => 1234, 'message' => 'Message'), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testSix') + ); + + $this->assertSame( + array('class' => 'Class', 'code' => 'ExceptionCode', 'message' => 'Message'), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testSeven') + ); + + $this->assertSame( + array('class' => 'Class', 'code' => 0, 'message' => 'Message'), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testEight') + ); + $this->assertSame( + array('class' => 'Class', 'code' => ExceptionTest::ERROR_CODE, 'message' => ExceptionTest::ERROR_MESSAGE), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testNine') + ); + $this->assertSame( + array('class' => 'Class', 'code' => My\Space\ExceptionNamespaceTest::ERROR_CODE, 'message' => My\Space\ExceptionNamespaceTest::ERROR_MESSAGE), + PHPUnit_Util_Test::getExpectedException('My\Space\ExceptionNamespaceTest', 'testConstants') + ); + // Ensure the Class::CONST expression is only evaluated when the constant really exists + $this->assertSame( + array('class' => 'Class', 'code' => 'ExceptionTest::UNKNOWN_CODE_CONSTANT', 'message' => 'ExceptionTest::UNKNOWN_MESSAGE_CONSTANT'), + PHPUnit_Util_Test::getExpectedException('ExceptionTest', 'testUnknownConstants') + ); + $this->assertSame( + array('class' => 'Class', 'code' => 'My\Space\ExceptionNamespaceTest::UNKNOWN_CODE_CONSTANT', 'message' => 'My\Space\ExceptionNamespaceTest::UNKNOWN_MESSAGE_CONSTANT'), + PHPUnit_Util_Test::getExpectedException('My\Space\ExceptionNamespaceTest', 'testUnknownConstants') + ); + } + + public function provideRequirements() + { + return array( + array('testOne', array()), + array('testTwo', array('PHPUnit' => '1.0')), + array('testThree', array('PHP' => '2.0')), + array('testFour', array('PHPUnit'=>'2.0', 'PHP' => '1.0')), + array('testFive', array('PHP' => '5.4.0RC6')), + array('testSix', array('PHP' => '5.4.0-alpha1')), + array('testSeven', array('PHP' => '5.4.0beta2')), + array('testEight', array('PHP' => '5.4-dev')), + array('testNine', array('functions' => array('testFunc'))), + array('testTen', array('extensions' => array('testExt'))), + array( + 'testAllPossibleRequirements', + array( + 'PHP' => '99-dev', + 'PHPUnit' => '9-dev', + 'functions' => array( + 'testFuncOne', + 'testFuncTwo', + ), + 'extensions' => array( + 'testExtOne', + 'testExtTwo', + ) + ) + ) + ); + } + + /** + * @dataProvider provideRequirements + */ + public function testGetRequirements($test, $result) + { + $this->assertEquals( + $result, + PHPUnit_Util_Test::getRequirements('RequirementsTest', $test) + ); + } + + public function testGetRequirementsMergesClassAndMethodDocBlocks() + { + $expectedAnnotations = array( + 'PHP' => '5.4', + 'PHPUnit' => '3.7', + 'functions' => array( + 'testFuncClass', + 'testFuncMethod', + ), + 'extensions' => array( + 'testExtClass', + 'testExtMethod', + ) + ); + + $this->assertEquals( + $expectedAnnotations, + PHPUnit_Util_Test::getRequirements('RequirementsClassDocBlockTest', 'testMethod') + ); + } + + public function testGetProvidedDataRegEx() + { + $result = preg_match(PHPUnit_Util_Test::REGEX_DATA_PROVIDER, '@dataProvider method', $matches); + $this->assertEquals(1, $result); + $this->assertEquals('method', $matches[1]); + + $result = preg_match(PHPUnit_Util_Test::REGEX_DATA_PROVIDER, '@dataProvider class::method', $matches); + $this->assertEquals(1, $result); + $this->assertEquals('class::method', $matches[1]); + + $result = preg_match(PHPUnit_Util_Test::REGEX_DATA_PROVIDER, '@dataProvider namespace\class::method', $matches); + $this->assertEquals(1, $result); + $this->assertEquals('namespace\class::method', $matches[1]); + + $result = preg_match(PHPUnit_Util_Test::REGEX_DATA_PROVIDER, '@dataProvider namespace\namespace\class::method', $matches); + $this->assertEquals(1, $result); + $this->assertEquals('namespace\namespace\class::method', $matches[1]); + + $result = preg_match(PHPUnit_Util_Test::REGEX_DATA_PROVIDER, '@dataProvider メソッド', $matches); + $this->assertEquals(1, $result); + $this->assertEquals('メソッド', $matches[1]); + } + + public function testParseAnnotation() + { + $this->assertEquals( + array('Foo', 'ほげ'), + PHPUnit_Util_Test::getDependencies(get_class($this), 'methodForTestParseAnnotation') + ); + } + + /** + * @depends Foo + * @depends ほげ + */ + public function methodForTestParseAnnotation() + { + } + + public function testParseAnnotationThatIsOnlyOneLine() + { + $this->assertEquals( + array('Bar'), + PHPUnit_Util_Test::getDependencies(get_class($this), 'methodForTestParseAnnotationThatIsOnlyOneLine') + ); + } + + /** @depends Bar */ + public function methodForTestParseAnnotationThatIsOnlyOneLine() + { + } +} diff --git a/vendor/phpunit/phpunit/Tests/Util/TypeTest.php b/vendor/phpunit/phpunit/Tests/Util/TypeTest.php new file mode 100644 index 0000000..808a713 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/TypeTest.php @@ -0,0 +1,269 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +require_once 'PHPUnit/Framework/TestCase.php'; + +require_once 'PHPUnit/Util/Type.php'; + +/** + * + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class Util_TypeTest extends PHPUnit_Framework_TestCase +{ + /** + * Removes spaces in front newlines + * + * @param string $string + * @return string + */ + public static function trimnl($string) + { + return preg_replace('/[ ]*\n/', "\n", $string); + } + + public function exportProvider() + { + $obj2 = new stdClass; + $obj2->foo = 'bar'; + + $obj = new stdClass; + //@codingStandardsIgnoreStart + $obj->null = NULL; + //@codingStandardsIgnoreEnd + $obj->boolean = TRUE; + $obj->integer = 1; + $obj->double = 1.2; + $obj->string = '1'; + $obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext"; + $obj->object = $obj2; + $obj->objectagain = $obj2; + $obj->array = array('foo' => 'bar'); + $obj->self = $obj; + + $array = array( + 0 => 0, + 'null' => NULL, + 'boolean' => TRUE, + 'integer' => 1, + 'double' => 1.2, + 'string' => '1', + 'text' => "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", + 'object' => $obj2, + 'objectagain' => $obj2, + 'array' => array('foo' => 'bar'), + ); + + $array['self'] = &$array; + + return array( + array(NULL, 'null'), + array(TRUE, 'true'), + array(1, '1'), + array(1.0, '1.0'), + array(1.2, '1.2'), + array('1', "'1'"), + // \n\r and \r is converted to \n + array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", +<< null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object (*RECURSION*) + 'array' => Array ( + 'foo' => 'bar' + ) + 'self' => stdClass Object (*RECURSION*) +) +EOF + ), + array(array(), 'Array ()'), + array($array, +<< 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object (*RECURSION*) + 'array' => Array ( + 'foo' => 'bar' + ) + 'self' => Array (*RECURSION*) +) +EOF + ), + array( + chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5), + 'Binary String: 0x000102030405' + ), + array( + implode('', array_map('chr', range(0x0e, 0x1f))), + 'Binary String: 0x0e0f101112131415161718191a1b1c1d1e1f' + ), + array( + chr(0x00) . chr(0x09), + 'Binary String: 0x0009' + ), + array( + '', + "''" + ), + ); + } + + /** + * @dataProvider exportProvider + */ + public function testExport($value, $expected) + { + $this->assertSame($expected, self::trimnl(PHPUnit_Util_Type::export($value))); + } + + public function shortenedExportProvider() + { + $obj = new stdClass; + $obj->foo = 'bar'; + + $array = array( + 'foo' => 'bar', + ); + + return array( + array(NULL, 'null'), + array(TRUE, 'true'), + array(1, '1'), + array(1.0, '1.0'), + array(1.2, '1.2'), + array('1', "'1'"), + // \n\r and \r is converted to \n + array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", "'this\\nis\\na\\nvery\\nvery\\nvery\\nvery...g\\ntext'"), + array(new stdClass, 'stdClass Object ()'), + array($obj, 'stdClass Object (...)'), + array(array(), 'Array ()'), + array($array, 'Array (...)'), + ); + } + + /** + * @dataProvider shortenedExportProvider + */ + public function testShortenedExport($value, $expected) + { + $this->assertSame($expected, self::trimnl(PHPUnit_Util_Type::shortenedExport($value))); + } + + public function provideNonBinaryMultibyteStrings() + { + return array( + array(implode('', array_map('chr', range(0x09, 0x0d))), 5), + array(implode('', array_map('chr', range(0x20, 0x7f))), 96), + array(implode('', array_map('chr', range(0x80, 0xff))), 128), + ); + } + + + /** + * @dataProvider provideNonBinaryMultibyteStrings + */ + public function testNonBinaryStringExport($value, $expectedLength) + { + $this->assertRegExp("~'.{{$expectedLength}}'\$~s", PHPUnit_Util_Type::export($value)); + } +} diff --git a/vendor/phpunit/phpunit/Tests/Util/XMLTest.php b/vendor/phpunit/phpunit/Tests/Util/XMLTest.php new file mode 100644 index 0000000..4a5b794 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/Util/XMLTest.php @@ -0,0 +1,323 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Mike Naberezny + * @author Derek DeVries + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.3.0 + */ + +/** + * + * + * @package PHPUnit + * @author Mike Naberezny + * @author Derek DeVries + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.3.0 + */ +class Util_XMLTest extends PHPUnit_Framework_TestCase +{ + public function testAssertValidKeysValidKeys() + { + $options = array('testA' => 1, 'testB' => 2, 'testC' => 3); + $valid = array('testA', 'testB', 'testC'); + $expected = array('testA' => 1, 'testB' => 2, 'testC' => 3); + $validated = PHPUnit_Util_XML::assertValidKeys($options, $valid); + + $this->assertEquals($expected, $validated); + } + + public function testAssertValidKeysValidKeysEmpty() + { + $options = array('testA' => 1, 'testB' => 2); + $valid = array('testA', 'testB', 'testC'); + $expected = array('testA' => 1, 'testB' => 2, 'testC' => NULL); + $validated = PHPUnit_Util_XML::assertValidKeys($options, $valid); + + $this->assertEquals($expected, $validated); + } + + public function testAssertValidKeysDefaultValuesA() + { + $options = array('testA' => 1, 'testB' => 2); + $valid = array('testA' => 23, 'testB' => 24, 'testC' => 25); + $expected = array('testA' => 1, 'testB' => 2, 'testC' => 25); + $validated = PHPUnit_Util_XML::assertValidKeys($options, $valid); + + $this->assertEquals($expected, $validated); + } + + public function testAssertValidKeysDefaultValuesB() + { + $options = array(); + $valid = array('testA' => 23, 'testB' => 24, 'testC' => 25); + $expected = array('testA' => 23, 'testB' => 24, 'testC' => 25); + $validated = PHPUnit_Util_XML::assertValidKeys($options, $valid); + + $this->assertEquals($expected, $validated); + } + + public function testAssertValidKeysInvalidKey() + { + $options = array('testA' => 1, 'testB' => 2, 'testD' => 3); + $valid = array('testA', 'testB', 'testC'); + + try { + $validated = PHPUnit_Util_XML::assertValidKeys($options, $valid); + $this->fail(); + } + + catch (PHPUnit_Framework_Exception $e) { + $this->assertEquals('Unknown key(s): testD', $e->getMessage()); + } + } + + public function testAssertValidKeysInvalidKeys() + { + $options = array('testA' => 1, 'testD' => 2, 'testE' => 3); + $valid = array('testA', 'testB', 'testC'); + + try { + $validated = PHPUnit_Util_XML::assertValidKeys($options, $valid); + $this->fail(); + } + + catch (PHPUnit_Framework_Exception $e) { + $this->assertEquals('Unknown key(s): testD, testE', $e->getMessage()); + } + } + + public function testConvertAssertSelect() + { + $selector = 'div#folder.open a[href="http://www.xerox.com"][title="xerox"].selected.big > span'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', + 'id' => 'folder', + 'class' => 'open', + 'descendant' => array('tag' => 'a', + 'class' => 'selected big', + 'attributes' => array('href' => 'http://www.xerox.com', + 'title' => 'xerox'), + 'child' => array('tag' => 'span'))); + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectElt() + { + $selector = 'div'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertClass() + { + $selector = '.foo'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('class' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertId() + { + $selector = '#foo'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('id' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertAttribute() + { + $selector = '[foo="bar"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('attributes' => array('foo' => 'bar')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertAttributeSpaces() + { + $selector = '[foo="bar baz"] div[value="foo bar"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('attributes' => array('foo' => 'bar baz'), + 'descendant' => array('tag' => 'div', + 'attributes' => array('value' => 'foo bar'))); + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertAttributeMultipleSpaces() + { + $selector = '[foo="bar baz"] div[value="foo bar baz"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('attributes' => array('foo' => 'bar baz'), + 'descendant' => array('tag' => 'div', + 'attributes' => array('value' => 'foo bar baz'))); + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltClass() + { + $selector = 'div.foo'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'class' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltId() + { + $selector = 'div#foo'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'id' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltAttrEqual() + { + $selector = 'div[foo="bar"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'attributes' => array('foo' => 'bar')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltMultiAttrEqual() + { + $selector = 'div[foo="bar"][baz="fob"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'attributes' => array('foo' => 'bar', 'baz' => 'fob')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltAttrHasOne() + { + $selector = 'div[foo~="bar"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'attributes' => array('foo' => 'regexp:/.*\bbar\b.*/')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltAttrContains() + { + $selector = 'div[foo*="bar"]'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'attributes' => array('foo' => 'regexp:/.*bar.*/')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltChild() + { + $selector = 'div > a'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'child' => array('tag' => 'a')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectEltDescendant() + { + $selector = 'div a'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector); + $tag = array('tag' => 'div', 'descendant' => array('tag' => 'a')); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectContent() + { + $selector = '#foo'; + $content = 'div contents'; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector, $content); + $tag = array('id' => 'foo', 'content' => 'div contents'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectTrue() + { + $selector = '#foo'; + $content = TRUE; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector, $content); + $tag = array('id' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertSelectFalse() + { + $selector = '#foo'; + $content = FALSE; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector, $content); + $tag = array('id' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertNumber() + { + $selector = '.foo'; + $content = 3; + $converted = PHPUnit_Util_XML::convertSelectToTag($selector, $content); + $tag = array('class' => 'foo'); + + $this->assertEquals($tag, $converted); + } + + public function testConvertAssertRange() + { + $selector = '#foo'; + $content = array('greater_than' => 5, 'less_than' => 10); + $converted = PHPUnit_Util_XML::convertSelectToTag($selector, $content); + $tag = array('id' => 'foo'); + + $this->assertEquals($tag, $converted); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/AbstractTest.php b/vendor/phpunit/phpunit/Tests/_files/AbstractTest.php new file mode 100644 index 0000000..556e7db --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/AbstractTest.php @@ -0,0 +1,7 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * An author. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class Author +{ + // the order of properties is important for testing the cycle! + public $books = array(); + + private $name = ''; + + public function __construct($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/vendor/phpunit/phpunit/Tests/_files/BankAccount.php b/vendor/phpunit/phpunit/Tests/_files/BankAccount.php new file mode 100644 index 0000000..518fa85 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/BankAccount.php @@ -0,0 +1,116 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +class BankAccountException extends RuntimeException {} + +/** + * A bank account. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.3.0 + */ +class BankAccount +{ + /** + * The bank account's balance. + * + * @var float + */ + protected $balance = 0; + + /** + * Returns the bank account's balance. + * + * @return float + */ + public function getBalance() + { + return $this->balance; + } + + /** + * Sets the bank account's balance. + * + * @param float $balance + * @throws BankAccountException + */ + protected function setBalance($balance) + { + if ($balance >= 0) { + $this->balance = $balance; + } else { + throw new BankAccountException; + } + } + + /** + * Deposits an amount of money to the bank account. + * + * @param float $balance + * @throws BankAccountException + */ + public function depositMoney($balance) + { + $this->setBalance($this->getBalance() + $balance); + + return $this->getBalance(); + } + + /** + * Withdraws an amount of money from the bank account. + * + * @param float $balance + * @throws BankAccountException + */ + public function withdrawMoney($balance) + { + $this->setBalance($this->getBalance() - $balance); + + return $this->getBalance(); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/BankAccountTest.php b/vendor/phpunit/phpunit/Tests/_files/BankAccountTest.php new file mode 100644 index 0000000..fe821c7 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/BankAccountTest.php @@ -0,0 +1,133 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +require_once 'PHPUnit/Framework/TestCase.php'; +require_once 'BankAccount.php'; + +/** + * Tests for the BankAccount class. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.3.0 + */ +class BankAccountTest extends PHPUnit_Framework_TestCase +{ + protected $ba; + + protected function setUp() + { + $this->ba = new BankAccount; + } + + /** + * @covers BankAccount::getBalance + * @group balanceIsInitiallyZero + * @group specification + */ + public function testBalanceIsInitiallyZero() + { + $this->assertEquals(0, $this->ba->getBalance()); + } + + /** + * @covers BankAccount::withdrawMoney + * @group balanceCannotBecomeNegative + * @group specification + */ + public function testBalanceCannotBecomeNegative() + { + try { + $this->ba->withdrawMoney(1); + } + + catch (BankAccountException $e) { + $this->assertEquals(0, $this->ba->getBalance()); + + return; + } + + $this->fail(); + } + + /** + * @covers BankAccount::depositMoney + * @group balanceCannotBecomeNegative + * @group specification + */ + public function testBalanceCannotBecomeNegative2() + { + try { + $this->ba->depositMoney(-1); + } + + catch (BankAccountException $e) { + $this->assertEquals(0, $this->ba->getBalance()); + + return; + } + + $this->fail(); + } + + /** + * @covers BankAccount::getBalance + * @covers BankAccount::depositMoney + * @covers BankAccount::withdrawMoney + * @group balanceCannotBecomeNegative + */ + /* + public function testDepositingAndWithdrawingMoneyWorks() + { + $this->assertEquals(0, $this->ba->getBalance()); + $this->ba->depositMoney(1); + $this->assertEquals(1, $this->ba->getBalance()); + $this->ba->withdrawMoney(1); + $this->assertEquals(0, $this->ba->getBalance()); + } + */ +} diff --git a/vendor/phpunit/phpunit/Tests/_files/BankAccountTest.test.php b/vendor/phpunit/phpunit/Tests/_files/BankAccountTest.test.php new file mode 100644 index 0000000..bb8cc25 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/BankAccountTest.test.php @@ -0,0 +1,133 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 2.3.0 + */ + +require_once 'PHPUnit/Framework/TestCase.php'; +require_once 'BankAccount.php'; + +/** + * Tests for the BankAccount class. + * + * @package PHPUnit + * @author Sebastian Bergmann + * @copyright 2002-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 2.3.0 + */ +class BankAccountWithCustomExtensionTest extends PHPUnit_Framework_TestCase +{ + protected $ba; + + protected function setUp() + { + $this->ba = new BankAccount; + } + + /** + * @covers BankAccount::getBalance + * @group balanceIsInitiallyZero + * @group specification + */ + public function testBalanceIsInitiallyZero() + { + $this->assertEquals(0, $this->ba->getBalance()); + } + + /** + * @covers BankAccount::withdrawMoney + * @group balanceCannotBecomeNegative + * @group specification + */ + public function testBalanceCannotBecomeNegative() + { + try { + $this->ba->withdrawMoney(1); + } + + catch (BankAccountException $e) { + $this->assertEquals(0, $this->ba->getBalance()); + + return; + } + + $this->fail(); + } + + /** + * @covers BankAccount::depositMoney + * @group balanceCannotBecomeNegative + * @group specification + */ + public function testBalanceCannotBecomeNegative2() + { + try { + $this->ba->depositMoney(-1); + } + + catch (BankAccountException $e) { + $this->assertEquals(0, $this->ba->getBalance()); + + return; + } + + $this->fail(); + } + + /** + * @covers BankAccount::getBalance + * @covers BankAccount::depositMoney + * @covers BankAccount::withdrawMoney + * @group balanceCannotBecomeNegative + */ + /* + public function testDepositingAndWithdrawingMoneyWorks() + { + $this->assertEquals(0, $this->ba->getBalance()); + $this->ba->depositMoney(1); + $this->assertEquals(1, $this->ba->getBalance()); + $this->ba->withdrawMoney(1); + $this->assertEquals(0, $this->ba->getBalance()); + } + */ +} diff --git a/vendor/phpunit/phpunit/Tests/_files/Book.php b/vendor/phpunit/phpunit/Tests/_files/Book.php new file mode 100644 index 0000000..7792578 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/Book.php @@ -0,0 +1,59 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * A book. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class Book +{ + // the order of properties is important for testing the cycle! + public $author = NULL; +} diff --git a/vendor/phpunit/phpunit/Tests/_files/Calculator.php b/vendor/phpunit/phpunit/Tests/_files/Calculator.php new file mode 100644 index 0000000..e269bd6 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/Calculator.php @@ -0,0 +1,14 @@ +assertTrue(TRUE); + } + +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ClassWithNonPublicAttributes.php b/vendor/phpunit/phpunit/Tests/_files/ClassWithNonPublicAttributes.php new file mode 100644 index 0000000..bb979e7 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ClassWithNonPublicAttributes.php @@ -0,0 +1,29 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.6.0 + */ + +/** + * A class with a __toString() method. + * + * @package PHPUnit + * @author Bernhard Schussek + * @copyright 2001-2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 3.6.0 + */ +class ClassWithToString +{ + public function __toString() + { + return 'string representation'; + } +} \ No newline at end of file diff --git a/vendor/phpunit/phpunit/Tests/_files/ConcreteTest.my.php b/vendor/phpunit/phpunit/Tests/_files/ConcreteTest.my.php new file mode 100644 index 0000000..ea9f8f1 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ConcreteTest.my.php @@ -0,0 +1,9 @@ +assertEquals($c, $a + $b); + } + + public static function providerMethod() + { + return array( + array(0, 0, 0), + array(0, 1, 1), + array(1, 1, 3), + array(1, 0, 1) + ); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/DependencyFailureTest.php b/vendor/phpunit/phpunit/Tests/_files/DependencyFailureTest.php new file mode 100644 index 0000000..d83aecd --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/DependencyFailureTest.php @@ -0,0 +1,22 @@ +fail(); + } + + /** + * @depends testOne + */ + public function testTwo() + { + } + + /** + * @depends testTwo + */ + public function testThree() + { + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/DependencySuccessTest.php b/vendor/phpunit/phpunit/Tests/_files/DependencySuccessTest.php new file mode 100644 index 0000000..0e4b5dd --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/DependencySuccessTest.php @@ -0,0 +1,21 @@ +addTestSuite('DependencySuccessTest'); + $suite->addTestSuite('DependencyFailureTest'); + + return $suite; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/DoubleTestCase.php b/vendor/phpunit/phpunit/Tests/_files/DoubleTestCase.php new file mode 100644 index 0000000..ba601a4 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/DoubleTestCase.php @@ -0,0 +1,25 @@ +testCase = $testCase; + } + + public function count() + { + return 2; + } + + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + $result->startTest($this); + + $this->testCase->runBare(); + $this->testCase->runBare(); + + $result->endTest($this, 0); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/EmptyTestCaseTest.php b/vendor/phpunit/phpunit/Tests/_files/EmptyTestCaseTest.php new file mode 100644 index 0000000..2a77bb2 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/EmptyTestCaseTest.php @@ -0,0 +1,4 @@ +setUp = TRUE; + } + + protected function assertPreConditions() + { + $this->assertPreConditions = TRUE; + } + + public function testSomething() + { + $this->testSomething = TRUE; + } + + protected function assertPostConditions() + { + $this->assertPostConditions = TRUE; + throw new Exception; + } + + protected function tearDown() + { + $this->tearDown = TRUE; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ExceptionInAssertPreConditionsTest.php b/vendor/phpunit/phpunit/Tests/_files/ExceptionInAssertPreConditionsTest.php new file mode 100644 index 0000000..01f571a --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ExceptionInAssertPreConditionsTest.php @@ -0,0 +1,35 @@ +setUp = TRUE; + } + + protected function assertPreConditions() + { + $this->assertPreConditions = TRUE; + throw new Exception; + } + + public function testSomething() + { + $this->testSomething = TRUE; + } + + protected function assertPostConditions() + { + $this->assertPostConditions = TRUE; + } + + protected function tearDown() + { + $this->tearDown = TRUE; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ExceptionInSetUpTest.php b/vendor/phpunit/phpunit/Tests/_files/ExceptionInSetUpTest.php new file mode 100644 index 0000000..e0861cd --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ExceptionInSetUpTest.php @@ -0,0 +1,35 @@ +setUp = TRUE; + throw new Exception; + } + + protected function assertPreConditions() + { + $this->assertPreConditions = TRUE; + } + + public function testSomething() + { + $this->testSomething = TRUE; + } + + protected function assertPostConditions() + { + $this->assertPostConditions = TRUE; + } + + protected function tearDown() + { + $this->tearDown = TRUE; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ExceptionInTearDownTest.php b/vendor/phpunit/phpunit/Tests/_files/ExceptionInTearDownTest.php new file mode 100644 index 0000000..3688dfc --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ExceptionInTearDownTest.php @@ -0,0 +1,35 @@ +setUp = TRUE; + } + + protected function assertPreConditions() + { + $this->assertPreConditions = TRUE; + } + + public function testSomething() + { + $this->testSomething = TRUE; + } + + protected function assertPostConditions() + { + $this->assertPostConditions = TRUE; + } + + protected function tearDown() + { + $this->tearDown = TRUE; + throw new Exception; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ExceptionInTest.php b/vendor/phpunit/phpunit/Tests/_files/ExceptionInTest.php new file mode 100644 index 0000000..2b1b66c --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ExceptionInTest.php @@ -0,0 +1,35 @@ +setUp = TRUE; + } + + protected function assertPreConditions() + { + $this->assertPreConditions = TRUE; + } + + public function testSomething() + { + $this->testSomething = TRUE; + throw new Exception; + } + + protected function assertPostConditions() + { + $this->assertPostConditions = TRUE; + } + + protected function tearDown() + { + $this->tearDown = TRUE; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ExceptionNamespaceTest.php b/vendor/phpunit/phpunit/Tests/_files/ExceptionNamespaceTest.php new file mode 100644 index 0000000..9644aae --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ExceptionNamespaceTest.php @@ -0,0 +1,38 @@ +assertEquals(array(1), array(2), 'message'); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $message = $e->getMessage() . "\n" . $e->getComparisonFailure()->getDiff(); + throw new ExceptionStackTestException("Child exception\n$message", 101, $e); + } + } + + public function testNestedExceptions() + { + $exceptionThree = new Exception('Three'); + $exceptionTwo = new InvalidArgumentException('Two', 0, $exceptionThree); + $exceptionOne = new Exception('One', 0, $exceptionTwo); + throw $exceptionOne; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/ExceptionTest.php b/vendor/phpunit/phpunit/Tests/_files/ExceptionTest.php new file mode 100644 index 0000000..3493743 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/ExceptionTest.php @@ -0,0 +1,97 @@ +fail(); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/FailureTest.php b/vendor/phpunit/phpunit/Tests/_files/FailureTest.php new file mode 100644 index 0000000..89a5843 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/FailureTest.php @@ -0,0 +1,76 @@ +assertEquals(array(1), array(2), 'message'); + } + + public function testAssertIntegerEqualsInteger() + { + $this->assertEquals(1, 2, 'message'); + } + + public function testAssertObjectEqualsObject() + { + $a = new StdClass; + $a->foo = 'bar'; + + $b = new StdClass; + $b->bar = 'foo'; + + $this->assertEquals($a, $b, 'message'); + } + + public function testAssertNullEqualsString() + { + $this->assertEquals(NULL, 'bar', 'message'); + } + + public function testAssertStringEqualsString() + { + $this->assertEquals('foo', 'bar', 'message'); + } + + public function testAssertTextEqualsText() + { + $this->assertEquals("foo\nbar\n", "foo\nbaz\n", 'message'); + } + + public function testAssertStringMatchesFormat() + { + $this->assertStringMatchesFormat('*%s*', '**', 'message'); + } + + public function testAssertNumericEqualsNumeric() + { + $this->assertEquals(1, 2, 'message'); + } + + public function testAssertTextSameText() + { + $this->assertSame('foo', 'bar', 'message'); + } + + public function testAssertObjectSameObject() + { + $this->assertSame(new StdClass, new StdClass, 'message'); + } + + public function testAssertObjectSameNull() + { + $this->assertSame(new StdClass, NULL, 'message'); + } + + public function testAssertFloatSameFloat() + { + $this->assertSame(1.0, 1.5, 'message'); + } + + // Note that due to the implementation of this assertion it counts as 2 asserts + public function testAssertStringMatchesFormatFile() + { + $this->assertStringMatchesFormatFile(__DIR__ . '/expectedFileFormat.txt', '...BAR...'); + } + +} diff --git a/vendor/phpunit/phpunit/Tests/_files/FatalTest.php b/vendor/phpunit/phpunit/Tests/_files/FatalTest.php new file mode 100644 index 0000000..6d07437 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/FatalTest.php @@ -0,0 +1,10 @@ +markTestIncomplete('Test incomplete'); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/InheritedTestCase.php b/vendor/phpunit/phpunit/Tests/_files/InheritedTestCase.php new file mode 100644 index 0000000..1a15eb6 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/InheritedTestCase.php @@ -0,0 +1,9 @@ +assertEquals('foo', $a); + $this->assertEquals('bar', $b); + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/NoArgTestCaseTest.php b/vendor/phpunit/phpunit/Tests/_files/NoArgTestCaseTest.php new file mode 100644 index 0000000..5867ba3 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/NoArgTestCaseTest.php @@ -0,0 +1,7 @@ +expectOutputString('foo'); + print 'foo'; + } + + public function testExpectOutputStringFooActualBar() + { + $this->expectOutputString('foo'); + print 'bar'; + } + + public function testExpectOutputRegexFooActualFoo() + { + $this->expectOutputRegex('/foo/'); + print 'foo'; + } + + public function testExpectOutputRegexFooActualBar() + { + $this->expectOutputRegex('/foo/'); + print 'bar'; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/OverrideTestCase.php b/vendor/phpunit/phpunit/Tests/_files/OverrideTestCase.php new file mode 100644 index 0000000..63ca294 --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/OverrideTestCase.php @@ -0,0 +1,9 @@ +a = $a; + $this->b = $b; + $this->c = $c; + } +} diff --git a/vendor/phpunit/phpunit/Tests/_files/SelectorAssertionsFixture.html b/vendor/phpunit/phpunit/Tests/_files/SelectorAssertionsFixture.html new file mode 100644 index 0000000..b8e908f --- /dev/null +++ b/vendor/phpunit/phpunit/Tests/_files/SelectorAssertionsFixture.html @@ -0,0 +1,44 @@ + + + + Login + +
      {name}{methods_bar}
      {methods_tested_percent}
      {methods_number}
      {crap}{lines_bar}
      {lines_executed_percent}
      {lines_number}
    d|V`}%hLO)+TPx5GZ|1VmQ?k`Ghd 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 W2xMeim*Bith0DYnkKn)kYya6hKV+c* literal 0 HcmV?d00001 diff --git a/modules/image/tests/kohana/ImageTest.php b/modules/image/tests/kohana/ImageTest.php new file mode 100644 index 0000000..967b764 --- /dev/null +++ b/modules/image/tests/kohana/ImageTest.php @@ -0,0 +1,36 @@ +markTestSkipped('The GD extension is not available.'); + } + } + + /** + * Tests the Image::save() method for files that don't have extensions + * + * @return void + */ + public function test_save_without_extension() + { + $image = Image::factory(MODPATH.'image/tests/test_data/test_image'); + $this->assertTrue($image->save(Kohana::$cache_dir.'/test_image')); + + unlink(Kohana::$cache_dir.'/test_image'); + } + +} // End Kohana_ImageTest diff --git a/modules/image/tests/test_data/test_image b/modules/image/tests/test_data/test_image new file mode 100644 index 0000000000000000000000000000000000000000..683a3c3e95a97f0f36a4b029d59840a51da473ce GIT binary patch literal 3455 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 AA^-pY literal 0 HcmV?d00001 diff --git a/modules/minion/README.md b/modules/minion/README.md new file mode 100644 index 0000000..6d317cb --- /dev/null +++ b/modules/minion/README.md @@ -0,0 +1,62 @@ +# Minion + +Minion is a framework for running tasks via the CLI. + +The system is inspired by ruckusing, which had a nice system for defining tasks but lacked the desired flexibility for kohana integration. + +## Getting Started + +First off, download and enable the module in your bootstrap + +Then copy the bash script `minion` alongside your index.php (most likely the webroot). +If you'd rather the executable be in a different location to index.php then simply modify the bash script to point to index.php. + +You can then run minion like so: + + ./minion {task} + +To view a list of minion tasks, run minion without any parameters, or with the `--help` option + + ./minion + ./minion --help + +To view help for a specific minion task run + + ./minion {task} --help + +For security reasons Minion will only run from the cli. Attempting to access it over http will cause +a `Kohana_Exception` to be thrown. + +If you're unable to use the binary file for whatever reason then simply replace `./minion {task}` in the above +examples with + + php index.php --uri=minion --task={task} + +## Writing your own tasks + +All minion tasks must be located in `classes/task/`. They can be in any module, thus allowing you to +ship custom minion tasks with your own module / product. + +Each task must extend the abstract class `Minion_Task` and implement `Minion_Task::_execute()`. + +See `Minion_Task` for more details. + +## Documentation + +Code should be commented well enough not to need documentation, and minion can extract a class' doccomment to use +as documentation on the cli. + +## Testing + +This module is unittested using the [unittest module](http://github.com/kohana/unittest). +You can use the `minion` group to only run minion tests. + +i.e. + + phpunit --group minion + +Feel free to contribute tests(!), they can be found in the `tests/minion` directory. :) + +## License + +This is licensed under the [same license as Kohana](http://kohanaframework.org/license). diff --git a/modules/minion/classes/Kohana/Minion/CLI.php b/modules/minion/classes/Kohana/Minion/CLI.php new file mode 100644 index 0000000..13c1de6 --- /dev/null +++ b/modules/minion/classes/Kohana/Minion/CLI.php @@ -0,0 +1,315 @@ + '0;30', + 'dark_gray' => '1;30', + 'blue' => '0;34', + 'light_blue' => '1;34', + 'green' => '0;32', + 'light_green' => '1;32', + 'cyan' => '0;36', + 'light_cyan' => '1;36', + 'red' => '0;31', + 'light_red' => '1;31', + 'purple' => '0;35', + 'light_purple' => '1;35', + 'brown' => '0;33', + 'yellow' => '1;33', + 'light_gray' => '0;37', + 'white' => '1;37', + ); + protected static $background_colors = array( + 'black' => '40', + 'red' => '41', + 'green' => '42', + 'yellow' => '43', + 'blue' => '44', + 'magenta' => '45', + 'cyan' => '46', + 'light_gray' => '47', + ); + + /** + * Returns one or more command-line options. Options are specified using + * standard CLI syntax: + * + * php index.php --username=john.smith --password=secret --var="some value with spaces" + * + * // Get the values of "username" and "password" + * $auth = Minion_CLI::options('username', 'password'); + * + * @param string $options,... option name + * @return array + */ + public static function options($options = NULL) + { + // Get all of the requested options + $options = func_get_args(); + + // Found option values + $values = array(); + + // Skip the first option, it is always the file executed + for ($i = 1; $i < $_SERVER['argc']; $i++) + { + if ( ! isset($_SERVER['argv'][$i])) + { + // No more args left + break; + } + + // Get the option + $opt = $_SERVER['argv'][$i]; + + if (substr($opt, 0, 2) !== '--') + { + // This is a positional argument + $values[] = $opt; + continue; + } + + // Remove the "--" prefix + $opt = substr($opt, 2); + + if (strpos($opt, '=')) + { + // Separate the name and value + list ($opt, $value) = explode('=', $opt, 2); + } + else + { + $value = NULL; + } + + $values[$opt] = $value; + } + + if ($options) + { + foreach ($values as $opt => $value) + { + if ( ! in_array($opt, $options)) + { + // Set the given value + unset($values[$opt]); + } + } + } + + return count($options) == 1 ? array_pop($values) : $values; + } + + /** + * Reads input from the user. This can have either 1 or 2 arguments. + * + * Usage: + * + * // Waits for any key press + * Minion_CLI::read(); + * + * // Takes any input + * $color = Minion_CLI::read('What is your favorite color?'); + * + * // Will only accept the options in the array + * $ready = Minion_CLI::read('Are you ready?', array('y','n')); + * + * @param string $text text to show user before waiting for input + * @param array $options array of options the user is shown + * @return string the user input + */ + public static function read($text = '', array $options = NULL) + { + // If a question has been asked with the read + $options_output = ''; + if ( ! empty($options)) + { + $options_output = ' [ '.implode(', ', $options).' ]'; + } + + fwrite(STDOUT, $text.$options_output.': '); + + // Read the input from keyboard. + $input = trim(fgets(STDIN)); + + // If options are provided and the choice is not in the array, tell them to try again + if ( ! empty($options) && ! in_array($input, $options)) + { + Minion_CLI::write('This is not a valid option. Please try again.'); + + $input = Minion_CLI::read($text, $options); + } + + // Read the input + return $input; + } + + /** + * Experimental feature. + * + * Reads hidden input from the user + * + * Usage: + * + * $password = Minion_CLI::password('Enter your password'); + * + * @author Mathew Davies. + * @return string + */ + public static function password($text = '') + { + $text .= ': '; + + if (Kohana::$is_windows) + { + $vbscript = sys_get_temp_dir().'Minion_CLI_Password.vbs'; + + // Create temporary file + file_put_contents($vbscript, 'wscript.echo(InputBox("'.addslashes($text).'"))'); + + $password = shell_exec('cscript //nologo '.escapeshellarg($command)); + + // Remove temporary file. + unlink($vbscript); + } + else + { + $password = shell_exec('/usr/bin/env bash -c \'read -s -p "'.escapeshellcmd($text).'" var && echo $var\''); + } + + Minion_CLI::write(); + + return trim($password); + } + + /** + * Outputs a string to the cli. If you send an array it will implode them + * with a line break. + * + * @param string|array $text the text to output, or array of lines + */ + public static function write($text = '') + { + if (is_array($text)) + { + foreach ($text as $line) + { + Minion_CLI::write($line); + } + } + else + { + fwrite(STDOUT, $text.PHP_EOL); + } + } + + /** + * Outputs a replacable line to the cli. You can continue replacing the + * line until `TRUE` is passed as the second parameter in order to indicate + * you are done modifying the line. + * + * // Sample progress indicator + * Minion_CLI::write_replace('0%'); + * Minion_CLI::write_replace('25%'); + * Minion_CLI::write_replace('50%'); + * Minion_CLI::write_replace('75%'); + * // Done writing this line + * Minion_CLI::write_replace('100%', TRUE); + * + * @param string $text the text to output + * @param boolean $end_line whether the line is done being replaced + */ + public static function write_replace($text = '', $end_line = FALSE) + { + // Append a newline if $end_line is TRUE + $text = $end_line ? $text.PHP_EOL : $text; + fwrite(STDOUT, "\r\033[K".$text); + } + + /** + * Waits a certain number of seconds, optionally showing a wait message and + * waiting for a key press. + * + * @author Fuel Development Team + * @license MIT License + * @copyright 2010 - 2011 Fuel Development Team + * @link http://fuelphp.com + * @param int $seconds number of seconds + * @param bool $countdown show a countdown or not + */ + public static function wait($seconds = 0, $countdown = false) + { + if ($countdown === true) + { + $time = $seconds; + + while ($time > 0) + { + fwrite(STDOUT, $time.'... '); + sleep(1); + $time--; + } + + Minion_CLI::write(); + } + else + { + if ($seconds > 0) + { + sleep($seconds); + } + else + { + Minion_CLI::write(Minion_CLI::$wait_msg); + Minion_CLI::read(); + } + } + } + + /** + * Returns the given text with the correct color codes for a foreground and + * optionally a background color. + * + * @author Fuel Development Team + * @license MIT License + * @copyright 2010 - 2011 Fuel Development Team + * @link http://fuelphp.com + * @param string $text the text to color + * @param atring $foreground the foreground color + * @param string $background the background color + * @return string the color coded string + */ + public static function color($text, $foreground, $background = null) + { + + if (Kohana::$is_windows) + { + return $text; + } + + if (!array_key_exists($foreground, Minion_CLI::$foreground_colors)) + { + throw new Kohana_Exception('Invalid CLI foreground color: '.$foreground); + } + + if ($background !== null and !array_key_exists($background, Minion_CLI::$background_colors)) + { + throw new Kohana_Exception('Invalid CLI background color: '.$background); + } + + $string = "\033[".Minion_CLI::$foreground_colors[$foreground]."m"; + + if ($background !== null) + { + $string .= "\033[".Minion_CLI::$background_colors[$background]."m"; + } + + $string .= $text."\033[0m"; + + return $string; + } + +} diff --git a/modules/minion/classes/Kohana/Minion/Exception.php b/modules/minion/classes/Kohana/Minion/Exception.php new file mode 100644 index 0000000..442e82d --- /dev/null +++ b/modules/minion/classes/Kohana/Minion/Exception.php @@ -0,0 +1,64 @@ +format_for_cli(); + } + else + { + echo Kohana_Exception::text($e); + } + + $exit_code = $e->getCode(); + + // Never exit "0" after an exception. + if ($exit_code == 0) + { + $exit_code = 1; + } + + exit($exit_code); + } + catch (Exception $e) + { + // Clean the output buffer if one exists + ob_get_level() and ob_clean(); + + // Display the exception text + echo Kohana_Exception::text($e), "\n"; + + // Exit with an error status + exit(1); + } + } + + public function format_for_cli() + { + return Kohana_Exception::text($e); + } +} diff --git a/modules/minion/classes/Kohana/Minion/Exception/InvalidTask.php b/modules/minion/classes/Kohana/Minion/Exception/InvalidTask.php new file mode 100644 index 0000000..db8ead4 --- /dev/null +++ b/modules/minion/classes/Kohana/Minion/Exception/InvalidTask.php @@ -0,0 +1,18 @@ +getMessage().PHP_EOL; + } + +} diff --git a/modules/minion/classes/Kohana/Minion/Task.php b/modules/minion/classes/Kohana/Minion/Task.php new file mode 100644 index 0000000..be4fcc0 --- /dev/null +++ b/modules/minion/classes/Kohana/Minion/Task.php @@ -0,0 +1,364 @@ + $class) + ); + } + + $class = new $class; + + if ( ! $class instanceof Minion_Task) + { + throw new Minion_Exception_InvalidTask( + "Task ':task' is not a valid minion task", + array(':task' => $class) + ); + } + + $class->set_options($options); + + // Show the help page for this task if requested + if (array_key_exists('help', $options)) + { + $class->_method = '_help'; + } + + return $class; + } + + /** + * The list of options this task accepts and their default values. + * + * protected $_options = array( + * 'limit' => 4, + * 'table' => NULL, + * ); + * + * @var array + */ + protected $_options = array(); + + /** + * Populated with the accepted options for this task. + * This array is automatically populated based on $_options. + * + * @var array + */ + protected $_accepted_options = array(); + + protected $_method = '_execute'; + + protected function __construct() + { + // Populate $_accepted_options based on keys from $_options + $this->_accepted_options = array_keys($this->_options); + } + + /** + * The file that get's passes to Validation::errors() when validation fails + * @var string|NULL + */ + protected $_errors_file = 'validation'; + + /** + * Gets the task name for the task + * + * @return string + */ + public function __toString() + { + static $task_name = NULL; + + if ($task_name === NULL) + { + $task_name = Minion_Task::convert_class_to_task($this); + } + + return $task_name; + } + + /** + * Sets options for this task + * + * $param array the array of options to set + * @return this + */ + public function set_options(array $options) + { + foreach ($options as $key => $value) + { + $this->_options[$key] = $value; + } + + return $this; + } + + /** + * Get the options that were passed into this task with their defaults + * + * @return array + */ + public function get_options() + { + return (array) $this->_options; + } + + /** + * Get a set of options that this task can accept + * + * @return array + */ + public function get_accepted_options() + { + return (array) $this->_accepted_options; + } + + /** + * Adds any validation rules/labels for validating _options + * + * public function build_validation(Validation $validation) + * { + * return parent::build_validation($validation) + * ->rule('paramname', 'not_empty'); // Require this param + * } + * + * @param Validation the validation object to add rules to + * + * @return Validation + */ + public function build_validation(Validation $validation) + { + // Add a rule to each key making sure it's in the task + foreach ($validation->as_array() as $key => $value) + { + $validation->rule($key, array($this, 'valid_option'), array(':validation', ':field')); + } + + return $validation; + } + + /** + * Returns $_errors_file + * + * @return string + */ + public function get_errors_file() + { + return $this->_errors_file; + } + + /** + * Execute the task with the specified set of options + * + * @return null + */ + public function execute() + { + $options = $this->get_options(); + + // Validate $options + $validation = Validation::factory($options); + $validation = $this->build_validation($validation); + + if ( $this->_method != '_help' AND ! $validation->check()) + { + echo View::factory('minion/error/validation') + ->set('task', Minion_Task::convert_class_to_task($this)) + ->set('errors', $validation->errors($this->get_errors_file())); + } + else + { + // Finally, run the task + $method = $this->_method; + echo $this->{$method}($options); + } + } + + abstract protected function _execute(array $params); + + /** + * Outputs help for this task + * + * @return null + */ + protected function _help(array $params) + { + $tasks = $this->_compile_task_list(Kohana::list_files('classes/task')); + + $inspector = new ReflectionClass($this); + + list($description, $tags) = $this->_parse_doccomment($inspector->getDocComment()); + + $view = View::factory('minion/help/task') + ->set('description', $description) + ->set('tags', (array) $tags) + ->set('task', Minion_Task::convert_class_to_task($this)); + + echo $view; + } + + + public function valid_option(Validation $validation, $option) + { + if ( ! in_array($option, $this->_accepted_options)) + { + $validation->error($option, 'minion_option'); + } + } + + /** + * Parses a doccomment, extracting both the comment and any tags associated + * + * Based on the code in Kodoc::parse() + * + * @param string The comment to parse + * @return array First element is the comment, second is an array of tags + */ + protected function _parse_doccomment($comment) + { + // Normalize all new lines to \n + $comment = str_replace(array("\r\n", "\n"), "\n", $comment); + + // Remove the phpdoc open/close tags and split + $comment = array_slice(explode("\n", $comment), 1, -1); + + // Tag content + $tags = array(); + + foreach ($comment as $i => $line) + { + // Remove all leading whitespace + $line = preg_replace('/^\s*\* ?/m', '', $line); + + // Search this line for a tag + if (preg_match('/^@(\S+)(?:\s*(.+))?$/', $line, $matches)) + { + // This is a tag line + unset($comment[$i]); + + $name = $matches[1]; + $text = isset($matches[2]) ? $matches[2] : ''; + + $tags[$name] = $text; + } + else + { + $comment[$i] = (string) $line; + } + } + + $comment = trim(implode("\n", $comment)); + + return array($comment, $tags); + } + + /** + * Compiles a list of available tasks from a directory structure + * + * @param array Directory structure of tasks + * @param string prefix + * @return array Compiled tasks + */ + protected function _compile_task_list(array $files, $prefix = '') + { + $output = array(); + + foreach ($files as $file => $path) + { + $file = substr($file, strrpos($file, DIRECTORY_SEPARATOR) + 1); + + if (is_array($path) AND count($path)) + { + $task = $this->_compile_task_list($path, $prefix.$file.Minion_Task::$task_separator); + + if ($task) + { + $output = array_merge($output, $task); + } + } + else + { + $output[] = strtolower($prefix.substr($file, 0, -strlen(EXT))); + } + } + + return $output; + } +} diff --git a/modules/minion/classes/Minion/CLI.php b/modules/minion/classes/Minion/CLI.php new file mode 100644 index 0000000..d998766 --- /dev/null +++ b/modules/minion/classes/Minion/CLI.php @@ -0,0 +1,3 @@ +_compile_task_list(Kohana::list_files('classes/Task')); + + $view = new View('minion/help/list'); + + $view->tasks = $tasks; + + echo $view; + } +} diff --git a/modules/minion/config/userguide.php b/modules/minion/config/userguide.php new file mode 100644 index 0000000..1bb7093 --- /dev/null +++ b/modules/minion/config/userguide.php @@ -0,0 +1,13 @@ + array( + 'minion' => array( + 'enabled' => TRUE, + 'name' => 'Minion', + 'description' => 'Minion is a simple command line task runner', + 'copyright' => '© 2009-2011 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/modules/minion/guide/minion/index.md b/modules/minion/guide/minion/index.md new file mode 100644 index 0000000..5c3be5b --- /dev/null +++ b/modules/minion/guide/minion/index.md @@ -0,0 +1,3 @@ +# Minion + +Minion is a simple command line task runner. \ No newline at end of file diff --git a/modules/minion/guide/minion/menu.md b/modules/minion/guide/minion/menu.md new file mode 100644 index 0000000..9c22809 --- /dev/null +++ b/modules/minion/guide/minion/menu.md @@ -0,0 +1,3 @@ +## [Minion]() + - [Setup](setup) + - [Writing a Task](tasks) \ No newline at end of file diff --git a/modules/minion/guide/minion/setup.md b/modules/minion/guide/minion/setup.md new file mode 100644 index 0000000..11e6256 --- /dev/null +++ b/modules/minion/guide/minion/setup.md @@ -0,0 +1,32 @@ +# Minion Setup + +To use minion, you'll need to make a small change to your index.php file: + + -/** + - * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. + - * If no source is specified, the URI will be automatically detected. + - */ + -echo Request::factory() + - ->execute() + - ->send_headers(TRUE) + - ->body(); + +if (PHP_SAPI == 'cli') // Try and load minion + +{ + + class_exists('Minion_Task') OR die('minion required!'); + + set_exception_handler(array('Kohana_Minion_Exception_Handler', 'handler')); + + + + Minion_Task::factory(Minion_CLI::options())->execute(); + +} + +else + +{ + + /** + + * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. + + * If no source is specified, the URI will be automatically detected. + + */ + + echo Request::factory() + + ->execute() + + ->send_headers(TRUE) + + ->body(); + +} + +This will short-circuit your index file to intercept any cli calls, and route them to the minion module. \ No newline at end of file diff --git a/modules/minion/guide/minion/tasks.md b/modules/minion/guide/minion/tasks.md new file mode 100644 index 0000000..4bda3ef --- /dev/null +++ b/modules/minion/guide/minion/tasks.md @@ -0,0 +1,71 @@ +# Writing Tasks + +Writing a task in minion is very easy. Simply create a new class called `Task_` and put it inside `classes/task/.php`. + + NULL, + ); + + /** + * This is a demo task + * + * @return null + */ + protected function _execute(array $params) + { + var_dump($params); + echo 'foobar'; + } + } + +You'll notice a few things here: + + - You need a main `_execute()` method. It should take one array parameter. + - This parameter contains any command line options passed to the task. + - For example, if you call the task above with `./minion --task=demo --foo=foobar` then `$params` will contain: `array('foo' => 'foobar', 'bar' => NULL)` + - It needs to have a `protected $_defaults` array. This is a list of parameters you want to accept for this task. Any parameters passed to the task not in this list will be rejected. + +## Namespacing Tasks + +You can "namespace" tasks by placing them all in a subdirectory: `classes/task/database/generate.php`. This task will be named `database:generate` and can be called with this task name. + +# Parameter Validations + +To add validations to your command line options, simply overload the `build_validation()` method in your task: + + public function build_validation(Validation $validation) + { + return parent::build_validation($validation) + ->rule('foo', 'not_empty') // Require this param + ->rule('bar', 'numeric'); // This param should be numeric + } + +These validations will run for every task call unless `--help` is passed to the task. + +# Task Help + +Tasks can have built-in help. Minion will read class docblocks that you specify: + + ':field is not a valid option for this task!', +); \ No newline at end of file diff --git a/modules/minion/minion b/modules/minion/minion new file mode 100644 index 0000000..dc4f755 --- /dev/null +++ b/modules/minion/minion @@ -0,0 +1,4 @@ +#!/usr/bin/env php + /dev/null 2>&1 + then + start_daemon + fi +done \ No newline at end of file diff --git a/modules/minion/tests/minion/task.php b/modules/minion/tests/minion/task.php new file mode 100644 index 0000000..e7918a0 --- /dev/null +++ b/modules/minion/tests/minion/task.php @@ -0,0 +1,70 @@ +assertSame($expected, Minion_Task::convert_task_to_class_name($task_name)); + } + + /** + * Provides test data for test_convert_class_to_task() + * + * @return array + */ + public function provider_convert_class_to_task() + { + return array( + array('db:migrate', 'Task_Db_Migrate'), + ); + } + + /** + * Tests that the task name can be found from a class name / object + * + * @test + * @covers Minion_Task::convert_class_to_task + * @dataProvider provider_convert_class_to_task + * @param string Expected task name + * @param mixed Input class + */ + public function test_convert_class_to_task($expected, $class) + { + $this->assertSame($expected, Minion_Task::convert_class_to_task($class)); + } +} diff --git a/modules/minion/views/minion/error/validation.php b/modules/minion/views/minion/error/validation.php new file mode 100644 index 0000000..a65758a --- /dev/null +++ b/modules/minion/views/minion/error/validation.php @@ -0,0 +1,10 @@ +Parameter Errors: + $error): ?> + - + + +Run + + php index.php --task= --help + +for more help \ No newline at end of file diff --git a/modules/minion/views/minion/help/error.php b/modules/minion/views/minion/help/error.php new file mode 100644 index 0000000..504115d --- /dev/null +++ b/modules/minion/views/minion/help/error.php @@ -0,0 +1,7 @@ + + +Run + + index.php --uri=minion + +for more help diff --git a/modules/minion/views/minion/help/list.php b/modules/minion/views/minion/help/list.php new file mode 100644 index 0000000..eeb06ef --- /dev/null +++ b/modules/minion/views/minion/help/list.php @@ -0,0 +1,17 @@ +Minion is a cli tool for performing tasks + +Usage + + {task} --task=[options] + +Where {task} is one of the following: + + + * + + + +For more information on what a task does and usage details execute + + --task={task} --help + diff --git a/modules/minion/views/minion/help/task.php b/modules/minion/views/minion/help/task.php new file mode 100644 index 0000000..8f2fcd7 --- /dev/null +++ b/modules/minion/views/minion/help/task.php @@ -0,0 +1,17 @@ + +Usage +======= +php minion.php --task= [--option1=value1] [--option2=value2] + +Details +======= + $tag_content): ?> +: + + + +Description +=========== + + + diff --git a/modules/orm/auth-schema-mysql.sql b/modules/orm/auth-schema-mysql.sql new file mode 100644 index 0000000..61a803a --- /dev/null +++ b/modules/orm/auth-schema-mysql.sql @@ -0,0 +1,49 @@ +CREATE TABLE IF NOT EXISTS `roles` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `name` varchar(32) NOT NULL, + `description` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation'); +INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.'); + +CREATE TABLE IF NOT EXISTS `roles_users` ( + `user_id` int(10) UNSIGNED NOT NULL, + `role_id` int(10) UNSIGNED NOT NULL, + PRIMARY KEY (`user_id`,`role_id`), + KEY `fk_role_id` (`role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `users` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `email` varchar(254) NOT NULL, + `username` varchar(32) NOT NULL DEFAULT '', + `password` varchar(64) NOT NULL, + `logins` int(10) UNSIGNED NOT NULL DEFAULT '0', + `last_login` int(10) UNSIGNED, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_username` (`username`), + UNIQUE KEY `uniq_email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `user_tokens` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` int(11) UNSIGNED NOT NULL, + `user_agent` varchar(40) NOT NULL, + `token` varchar(40) NOT NULL, + `created` int(10) UNSIGNED NOT NULL, + `expires` int(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_token` (`token`), + KEY `fk_user_id` (`user_id`), + KEY `expires` (`expires`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `roles_users` + ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE; + +ALTER TABLE `user_tokens` + ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE; diff --git a/modules/orm/auth-schema-postgresql.sql b/modules/orm/auth-schema-postgresql.sql new file mode 100644 index 0000000..ce82ed1 --- /dev/null +++ b/modules/orm/auth-schema-postgresql.sql @@ -0,0 +1,53 @@ +CREATE TABLE roles +( + id serial, + "name" varchar(32) NOT NULL, + description text NOT NULL, + CONSTRAINT roles_id_pkey PRIMARY KEY (id), + CONSTRAINT roles_name_key UNIQUE (name) +); + +CREATE TABLE roles_users +( + user_id integer, + role_id integer +); + +CREATE TABLE users +( + id serial, + email varchar(254) NOT NULL, + username varchar(32) NOT NULL, + "password" varchar(64) NOT NULL, + logins integer NOT NULL DEFAULT 0, + last_login integer, + CONSTRAINT users_id_pkey PRIMARY KEY (id), + CONSTRAINT users_username_key UNIQUE (username), + CONSTRAINT users_email_key UNIQUE (email), + CONSTRAINT users_logins_check CHECK (logins >= 0) +); + +CREATE TABLE user_tokens +( + id serial, + user_id integer NOT NULL, + user_agent varchar(40) NOT NULL, + token character varying(32) NOT NULL, + created integer NOT NULL, + expires integer NOT NULL, + CONSTRAINT user_tokens_id_pkey PRIMARY KEY (id), + CONSTRAINT user_tokens_token_key UNIQUE (token) +); + +CREATE INDEX user_id_idx ON roles_users (user_id); +CREATE INDEX role_id_idx ON roles_users (role_id); + +ALTER TABLE roles_users + ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + ADD CONSTRAINT role_id_fkey FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE; + +ALTER TABLE user_tokens + ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + +INSERT INTO roles (name, description) VALUES ('login', 'Login privileges, granted after account confirmation'); +INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.'); diff --git a/modules/orm/classes/Auth/ORM.php b/modules/orm/classes/Auth/ORM.php new file mode 100644 index 0000000..590bef1 --- /dev/null +++ b/modules/orm/classes/Auth/ORM.php @@ -0,0 +1,3 @@ +get_user(); + + if ( ! $user) + return FALSE; + + if ($user instanceof Model_User AND $user->loaded()) + { + // If we don't have a roll no further checking is needed + if ( ! $role) + return TRUE; + + if (is_array($role)) + { + // Get all the roles + $roles = ORM::factory('Role') + ->where('name', 'IN', $role) + ->find_all() + ->as_array(NULL, 'id'); + + // Make sure all the roles are valid ones + if (count($roles) !== count($role)) + return FALSE; + } + else + { + if ( ! is_object($role)) + { + // Load the role + $roles = ORM::factory('Role', array('name' => $role)); + + if ( ! $roles->loaded()) + return FALSE; + } + } + + return $user->has('roles', $roles); + } + } + + /** + * Logs a user in. + * + * @param string $username + * @param string $password + * @param boolean $remember enable autologin + * @return boolean + */ + protected function _login($user, $password, $remember) + { + if ( ! is_object($user)) + { + $username = $user; + + // Load the user + $user = ORM::factory('User'); + $user->where($user->unique_key($username), '=', $username)->find(); + } + + if (is_string($password)) + { + // Create a hashed password + $password = $this->hash($password); + } + + // If the passwords match, perform a login + if ($user->has('roles', ORM::factory('Role', array('name' => 'login'))) AND $user->password === $password) + { + if ($remember === TRUE) + { + // Token data + $data = array( + 'user_id' => $user->pk(), + 'expires' => time() + $this->_config['lifetime'], + 'user_agent' => sha1(Request::$user_agent), + ); + + // Create a new autologin token + $token = ORM::factory('User_Token') + ->values($data) + ->create(); + + // Set the autologin cookie + Cookie::set('authautologin', $token->token, $this->_config['lifetime']); + } + + // Finish the login + $this->complete_login($user); + + return TRUE; + } + + // Login failed + return FALSE; + } + + /** + * Forces a user to be logged in, without specifying a password. + * + * @param mixed $user username string, or user ORM object + * @param boolean $mark_session_as_forced mark the session as forced + * @return boolean + */ + public function force_login($user, $mark_session_as_forced = FALSE) + { + if ( ! is_object($user)) + { + $username = $user; + + // Load the user + $user = ORM::factory('User'); + $user->where($user->unique_key($username), '=', $username)->find(); + } + + if ($mark_session_as_forced === TRUE) + { + // Mark the session as forced, to prevent users from changing account information + $this->_session->set('auth_forced', TRUE); + } + + // Run the standard completion + $this->complete_login($user); + } + + /** + * Logs a user in, based on the authautologin cookie. + * + * @return mixed + */ + public function auto_login() + { + if ($token = Cookie::get('authautologin')) + { + // Load the token and user + $token = ORM::factory('User_Token', array('token' => $token)); + + if ($token->loaded() AND $token->user->loaded()) + { + if ($token->user_agent === sha1(Request::$user_agent)) + { + // Save the token to create a new unique token + $token->save(); + + // Set the new token + Cookie::set('authautologin', $token->token, $token->expires - time()); + + // Complete the login with the found data + $this->complete_login($token->user); + + // Automatic login was successful + return $token->user; + } + + // Token is invalid + $token->delete(); + } + } + + return FALSE; + } + + /** + * Gets the currently logged in user from the session (with auto_login check). + * Returns $default if no user is currently logged in. + * + * @param mixed $default to return in case user isn't logged in + * @return mixed + */ + public function get_user($default = NULL) + { + $user = parent::get_user($default); + + if ($user === $default) + { + // check for "remembered" login + if (($user = $this->auto_login()) === FALSE) + return $default; + } + + return $user; + } + + /** + * Log a user out and remove any autologin cookies. + * + * @param boolean $destroy completely destroy the session + * @param boolean $logout_all remove all tokens for user + * @return boolean + */ + public function logout($destroy = FALSE, $logout_all = FALSE) + { + // Set by force_login() + $this->_session->delete('auth_forced'); + + if ($token = Cookie::get('authautologin')) + { + // Delete the autologin cookie to prevent re-login + Cookie::delete('authautologin'); + + // Clear the autologin token from the database + $token = ORM::factory('User_Token', array('token' => $token)); + + if ($token->loaded() AND $logout_all) + { + // Delete all user tokens. This isn't the most elegant solution but does the job + $tokens = ORM::factory('User_Token')->where('user_id','=',$token->user_id)->find_all(); + + foreach ($tokens as $_token) + { + $_token->delete(); + } + } + elseif ($token->loaded()) + { + $token->delete(); + } + } + + return parent::logout($destroy); + } + + /** + * Get the stored password for a username. + * + * @param mixed $user username string, or user ORM object + * @return string + */ + public function password($user) + { + if ( ! is_object($user)) + { + $username = $user; + + // Load the user + $user = ORM::factory('User'); + $user->where($user->unique_key($username), '=', $username)->find(); + } + + return $user->password; + } + + /** + * Complete the login for a user by incrementing the logins and setting + * session data: user_id, username, roles. + * + * @param object $user user ORM object + * @return void + */ + protected function complete_login($user) + { + $user->complete_login(); + + return parent::complete_login($user); + } + + /** + * Compare password with original (hashed). Works for current (logged in) user + * + * @param string $password + * @return boolean + */ + public function check_password($password) + { + $user = $this->get_user(); + + if ( ! $user) + return FALSE; + + return ($this->hash($password) === $user->password); + } + +} // End Auth ORM diff --git a/modules/orm/classes/Kohana/ORM.php b/modules/orm/classes/Kohana/ORM.php new file mode 100644 index 0000000..716e51d --- /dev/null +++ b/modules/orm/classes/Kohana/ORM.php @@ -0,0 +1,2354 @@ +_initialize(); + + if ($id !== NULL) + { + if (is_array($id)) + { + foreach ($id as $column => $value) + { + // Passing an array of column => values + $this->where($column, '=', $value); + } + + $this->find(); + } + else + { + // Passing the primary key + $this->where($this->_object_name.'.'.$this->_primary_key, '=', $id)->find(); + } + } + elseif ( ! empty($this->_cast_data)) + { + // Load preloaded data from a database call cast + $this->_load_values($this->_cast_data); + + $this->_cast_data = array(); + } + } + + /** + * Prepares the model database connection, determines the table name, + * and loads column information. + * + * @return void + */ + protected function _initialize() + { + // Set the object name and plural name + $this->_object_name = strtolower(substr(get_class($this), 6)); + + // Check if this model has already been initialized + if ( ! $init = Arr::get(ORM::$_init_cache, $this->_object_name, FALSE)) + { + $init = array( + '_belongs_to' => array(), + '_has_one' => array(), + '_has_many' => array(), + ); + + // Set the object plural name if none predefined + if ( ! isset($this->_object_plural)) + { + $init['_object_plural'] = Inflector::plural($this->_object_name); + } + + if ( ! $this->_errors_filename) + { + $init['_errors_filename'] = $this->_object_name; + } + + if ( ! is_object($this->_db)) + { + // Get database instance + $init['_db'] = Database::instance($this->_db_group); + } + + if (empty($this->_table_name)) + { + // Table name is the same as the object name + $init['_table_name'] = $this->_object_name; + + if ($this->_table_names_plural === TRUE) + { + // Make the table name plural + $init['_table_name'] = Arr::get($init, '_object_plural', $this->_object_plural); + } + } + + $defaults = array(); + + foreach ($this->_belongs_to as $alias => $details) + { + if ( ! isset($details['model'])) + { + $defaults['model'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $alias))); + } + + $defaults['foreign_key'] = $alias.$this->_foreign_key_suffix; + + $init['_belongs_to'][$alias] = array_merge($defaults, $details); + } + + foreach ($this->_has_one as $alias => $details) + { + if ( ! isset($details['model'])) + { + $defaults['model'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $alias))); + } + + $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix; + + $init['_has_one'][$alias] = array_merge($defaults, $details); + } + + foreach ($this->_has_many as $alias => $details) + { + if ( ! isset($details['model'])) + { + $defaults['model'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', Inflector::singular($alias)))); + } + + $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix; + $defaults['through'] = NULL; + + if ( ! isset($details['far_key'])) + { + $defaults['far_key'] = Inflector::singular($alias).$this->_foreign_key_suffix; + } + + $init['_has_many'][$alias] = array_merge($defaults, $details); + } + + ORM::$_init_cache[$this->_object_name] = $init; + } + + // Assign initialized properties to the current object + foreach ($init as $property => $value) + { + $this->{$property} = $value; + } + + // Load column information + $this->reload_columns(); + + // Clear initial model state + $this->clear(); + } + + /** + * Initializes validation rules, and labels + * + * @return void + */ + protected function _validation() + { + // Build the validation object with its rules + $this->_validation = Validation::factory($this->_object) + ->bind(':model', $this) + ->bind(':original_values', $this->_original_values) + ->bind(':changed', $this->_changed); + + foreach ($this->rules() as $field => $rules) + { + $this->_validation->rules($field, $rules); + } + + // Use column names by default for labels + $columns = array_keys($this->_table_columns); + + // Merge user-defined labels + $labels = array_merge(array_combine($columns, $columns), $this->labels()); + + foreach ($labels as $field => $label) + { + $this->_validation->label($field, $label); + } + } + + /** + * Reload column definitions. + * + * @chainable + * @param boolean $force Force reloading + * @return ORM + */ + public function reload_columns($force = FALSE) + { + if ($force === TRUE OR empty($this->_table_columns)) + { + if (isset(ORM::$_column_cache[$this->_object_name])) + { + // Use cached column information + $this->_table_columns = ORM::$_column_cache[$this->_object_name]; + } + else + { + // Grab column information from database + $this->_table_columns = $this->list_columns(); + + // Load column cache + ORM::$_column_cache[$this->_object_name] = $this->_table_columns; + } + } + + return $this; + } + + /** + * Unloads the current object and clears the status. + * + * @chainable + * @return ORM + */ + public function clear() + { + // Create an array with all the columns set to NULL + $values = array_combine(array_keys($this->_table_columns), array_fill(0, count($this->_table_columns), NULL)); + + // Replace the object and reset the object status + $this->_object = $this->_changed = $this->_related = $this->_original_values = array(); + + // Replace the current object with an empty one + $this->_load_values($values); + + // Reset primary key + $this->_primary_key_value = NULL; + + // Reset the loaded state + $this->_loaded = FALSE; + + $this->reset(); + + return $this; + } + + /** + * Reloads the current object from the database. + * + * @chainable + * @return ORM + */ + public function reload() + { + $primary_key = $this->pk(); + + // Replace the object and reset the object status + $this->_object = $this->_changed = $this->_related = $this->_original_values = array(); + + // Only reload the object if we have one to reload + if ($this->_loaded) + return $this->clear() + ->where($this->_object_name.'.'.$this->_primary_key, '=', $primary_key) + ->find(); + else + return $this->clear(); + } + + /** + * Checks if object data is set. + * + * @param string $column Column name + * @return boolean + */ + public function __isset($column) + { + return (isset($this->_object[$column]) OR + isset($this->_related[$column]) OR + isset($this->_has_one[$column]) OR + isset($this->_belongs_to[$column]) OR + isset($this->_has_many[$column])); + } + + /** + * Unsets object data. + * + * @param string $column Column name + * @return void + */ + public function __unset($column) + { + unset($this->_object[$column], $this->_changed[$column], $this->_related[$column]); + } + + /** + * Displays the primary key of a model when it is converted to a string. + * + * @return string + */ + public function __toString() + { + return (string) $this->pk(); + } + + /** + * Allows serialization of only the object data and state, to prevent + * "stale" objects being unserialized, which also requires less memory. + * + * @return string + */ + public function serialize() + { + // Store only information about the object + foreach (array('_primary_key_value', '_object', '_changed', '_loaded', '_saved', '_sorting', '_original_values') as $var) + { + $data[$var] = $this->{$var}; + } + + return serialize($data); + } + + /** + * Check whether the model data has been modified. + * If $field is specified, checks whether that field was modified. + * + * @param string $field field to check for changes + * @return bool Whether or not the field has changed + */ + public function changed($field = NULL) + { + return ($field === NULL) + ? $this->_changed + : Arr::get($this->_changed, $field); + } + + /** + * Prepares the database connection and reloads the object. + * + * @param string $data String for unserialization + * @return void + */ + public function unserialize($data) + { + // Initialize model + $this->_initialize(); + + foreach (unserialize($data) as $name => $var) + { + $this->{$name} = $var; + } + + if ($this->_reload_on_wakeup === TRUE) + { + // Reload the object + $this->reload(); + } + } + + /** + * Handles retrieval of all model values, relationships, and metadata. + * [!!] This should not be overridden. + * + * @param string $column Column name + * @return mixed + */ + public function __get($column) + { + return $this->get($column); + } + + /** + * Handles getting of column + * Override this method to add custom get behavior + * + * @param string $column Column name + * @throws Kohana_Exception + * @return mixed + */ + public function get($column) + { + if (array_key_exists($column, $this->_object)) + { + return (in_array($column, $this->_serialize_columns)) + ? $this->_unserialize_value($this->_object[$column]) + : $this->_object[$column]; + } + elseif (isset($this->_related[$column])) + { + // Return related model that has already been fetched + return $this->_related[$column]; + } + elseif (isset($this->_belongs_to[$column])) + { + $model = $this->_related($column); + + // Use this model's column and foreign model's primary key + $col = $model->_object_name.'.'.$model->_primary_key; + $val = $this->_object[$this->_belongs_to[$column]['foreign_key']]; + + // Make sure we don't run WHERE "AUTO_INCREMENT column" = NULL queries. This would + // return the last inserted record instead of an empty result. + // See: http://mysql.localhost.net.ar/doc/refman/5.1/en/server-session-variables.html#sysvar_sql_auto_is_null + if ($val !== NULL) + { + $model->where($col, '=', $val)->find(); + } + + return $this->_related[$column] = $model; + } + elseif (isset($this->_has_one[$column])) + { + $model = $this->_related($column); + + // Use this model's primary key value and foreign model's column + $col = $model->_object_name.'.'.$this->_has_one[$column]['foreign_key']; + $val = $this->pk(); + + $model->where($col, '=', $val)->find(); + + return $this->_related[$column] = $model; + } + elseif (isset($this->_has_many[$column])) + { + $model = ORM::factory($this->_has_many[$column]['model']); + + if (isset($this->_has_many[$column]['through'])) + { + // Grab has_many "through" relationship table + $through = $this->_has_many[$column]['through']; + + // Join on through model's target foreign key (far_key) and target model's primary key + $join_col1 = $through.'.'.$this->_has_many[$column]['far_key']; + $join_col2 = $model->_object_name.'.'.$model->_primary_key; + + $model->join($through)->on($join_col1, '=', $join_col2); + + // Through table's source foreign key (foreign_key) should be this model's primary key + $col = $through.'.'.$this->_has_many[$column]['foreign_key']; + $val = $this->pk(); + } + else + { + // Simple has_many relationship, search where target model's foreign key is this model's primary key + $col = $model->_object_name.'.'.$this->_has_many[$column]['foreign_key']; + $val = $this->pk(); + } + + return $model->where($col, '=', $val); + } + else + { + throw new Kohana_Exception('The :property property does not exist in the :class class', + array(':property' => $column, ':class' => get_class($this))); + } + } + + /** + * Base set method. + * [!!] This should not be overridden. + * + * @param string $column Column name + * @param mixed $value Column value + * @return void + */ + public function __set($column, $value) + { + $this->set($column, $value); + } + + /** + * Handles setting of columns + * Override this method to add custom set behavior + * + * @param string $column Column name + * @param mixed $value Column value + * @throws Kohana_Exception + * @return ORM + */ + public function set($column, $value) + { + if ( ! isset($this->_object_name)) + { + // Object not yet constructed, so we're loading data from a database call cast + $this->_cast_data[$column] = $value; + + return $this; + } + + if (in_array($column, $this->_serialize_columns)) + { + $value = $this->_serialize_value($value); + } + + if (array_key_exists($column, $this->_object)) + { + // Filter the data + $value = $this->run_filter($column, $value); + + // See if the data really changed + if ($value !== $this->_object[$column]) + { + $this->_object[$column] = $value; + + // Data has changed + $this->_changed[$column] = $column; + + // Object is no longer saved or valid + $this->_saved = $this->_valid = FALSE; + } + } + elseif (isset($this->_belongs_to[$column])) + { + // Update related object itself + $this->_related[$column] = $value; + + // Update the foreign key of this model + $this->_object[$this->_belongs_to[$column]['foreign_key']] = ($value instanceof ORM) + ? $value->pk() + : NULL; + + $this->_changed[$column] = $this->_belongs_to[$column]['foreign_key']; + } + else + { + throw new Kohana_Exception('The :property: property does not exist in the :class: class', + array(':property:' => $column, ':class:' => get_class($this))); + } + + return $this; + } + + /** + * Set values from an array with support for one-one relationships. This method should be used + * for loading in post data, etc. + * + * @param array $values Array of column => val + * @param array $expected Array of keys to take from $values + * @return ORM + */ + public function values(array $values, array $expected = NULL) + { + // Default to expecting everything except the primary key + if ($expected === NULL) + { + $expected = array_keys($this->_table_columns); + + // Don't set the primary key by default + unset($values[$this->_primary_key]); + } + + foreach ($expected as $key => $column) + { + if (is_string($key)) + { + // isset() fails when the value is NULL (we want it to pass) + if ( ! array_key_exists($key, $values)) + continue; + + // Try to set values to a related model + $this->{$key}->values($values[$key], $column); + } + else + { + // isset() fails when the value is NULL (we want it to pass) + if ( ! array_key_exists($column, $values)) + continue; + + // Update the column, respects __set() + $this->$column = $values[$column]; + } + } + + return $this; + } + + /** + * Returns the values of this object as an array, including any related one-one + * models that have already been loaded using with() + * + * @return array + */ + public function as_array() + { + $object = array(); + + foreach ($this->_object as $column => $value) + { + // Call __get for any user processing + $object[$column] = $this->__get($column); + } + + foreach ($this->_related as $column => $model) + { + // Include any related objects that are already loaded + $object[$column] = $model->as_array(); + } + + return $object; + } + + /** + * Binds another one-to-one object to this model. One-to-one objects + * can be nested using 'object1:object2' syntax + * + * @param string $target_path Target model to bind to + * @return ORM + */ + public function with($target_path) + { + if (isset($this->_with_applied[$target_path])) + { + // Don't join anything already joined + return $this; + } + + // Split object parts + $aliases = explode(':', $target_path); + $target = $this; + foreach ($aliases as $alias) + { + // Go down the line of objects to find the given target + $parent = $target; + $target = $parent->_related($alias); + + if ( ! $target) + { + // Can't find related object + return $this; + } + } + + // Target alias is at the end + $target_alias = $alias; + + // Pop-off top alias to get the parent path (user:photo:tag becomes user:photo - the parent table prefix) + array_pop($aliases); + $parent_path = implode(':', $aliases); + + if (empty($parent_path)) + { + // Use this table name itself for the parent path + $parent_path = $this->_object_name; + } + else + { + if ( ! isset($this->_with_applied[$parent_path])) + { + // If the parent path hasn't been joined yet, do it first (otherwise LEFT JOINs fail) + $this->with($parent_path); + } + } + + // Add to with_applied to prevent duplicate joins + $this->_with_applied[$target_path] = TRUE; + + // Use the keys of the empty object to determine the columns + foreach (array_keys($target->_object) as $column) + { + $name = $target_path.'.'.$column; + $alias = $target_path.':'.$column; + + // Add the prefix so that load_result can determine the relationship + $this->select(array($name, $alias)); + } + + if (isset($parent->_belongs_to[$target_alias])) + { + // Parent belongs_to target, use target's primary key and parent's foreign key + $join_col1 = $target_path.'.'.$target->_primary_key; + $join_col2 = $parent_path.'.'.$parent->_belongs_to[$target_alias]['foreign_key']; + } + else + { + // Parent has_one target, use parent's primary key as target's foreign key + $join_col1 = $parent_path.'.'.$parent->_primary_key; + $join_col2 = $target_path.'.'.$parent->_has_one[$target_alias]['foreign_key']; + } + + // Join the related object into the result + $this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2); + + return $this; + } + + /** + * Initializes the Database Builder to given query type + * + * @param integer $type Type of Database query + * @return ORM + */ + protected function _build($type) + { + // Construct new builder object based on query type + switch ($type) + { + case Database::SELECT: + $this->_db_builder = DB::select(); + break; + case Database::UPDATE: + $this->_db_builder = DB::update(array($this->_table_name, $this->_object_name)); + break; + case Database::DELETE: + // Cannot use an alias for DELETE queries + $this->_db_builder = DB::delete($this->_table_name); + } + + // Process pending database method calls + foreach ($this->_db_pending as $method) + { + $name = $method['name']; + $args = $method['args']; + + $this->_db_applied[$name] = $name; + + call_user_func_array(array($this->_db_builder, $name), $args); + } + + return $this; + } + + /** + * Finds and loads a single database row into the object. + * + * @chainable + * @throws Kohana_Exception + * @return ORM + */ + public function find() + { + if ($this->_loaded) + throw new Kohana_Exception('Method find() cannot be called on loaded objects'); + + if ( ! empty($this->_load_with)) + { + foreach ($this->_load_with as $alias) + { + // Bind auto relationships + $this->with($alias); + } + } + + $this->_build(Database::SELECT); + + return $this->_load_result(FALSE); + } + + /** + * Finds multiple database rows and returns an iterator of the rows found. + * + * @throws Kohana_Exception + * @return Database_Result + */ + public function find_all() + { + if ($this->_loaded) + throw new Kohana_Exception('Method find_all() cannot be called on loaded objects'); + + if ( ! empty($this->_load_with)) + { + foreach ($this->_load_with as $alias) + { + // Bind auto relationships + $this->with($alias); + } + } + + $this->_build(Database::SELECT); + + return $this->_load_result(TRUE); + } + + /** + * Returns an array of columns to include in the select query. This method + * can be overridden to change the default select behavior. + * + * @return array Columns to select + */ + protected function _build_select() + { + $columns = array(); + + foreach ($this->_table_columns as $column => $_) + { + $columns[] = array($this->_object_name.'.'.$column, $column); + } + + return $columns; + } + + /** + * Loads a database result, either as a new record for this model, or as + * an iterator for multiple rows. + * + * @chainable + * @param bool $multiple Return an iterator or load a single row + * @return ORM|Database_Result + */ + protected function _load_result($multiple = FALSE) + { + $this->_db_builder->from(array($this->_table_name, $this->_object_name)); + + if ($multiple === FALSE) + { + // Only fetch 1 record + $this->_db_builder->limit(1); + } + + // Select all columns by default + $this->_db_builder->select_array($this->_build_select()); + + if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting)) + { + foreach ($this->_sorting as $column => $direction) + { + if (strpos($column, '.') === FALSE) + { + // Sorting column for use in JOINs + $column = $this->_object_name.'.'.$column; + } + + $this->_db_builder->order_by($column, $direction); + } + } + + if ($multiple === TRUE) + { + // Return database iterator casting to this object type + $result = $this->_db_builder->as_object(get_class($this))->execute($this->_db); + + $this->reset(); + + return $result; + } + else + { + // Load the result as an associative array + $result = $this->_db_builder->as_assoc()->execute($this->_db); + + $this->reset(); + + if ($result->count() === 1) + { + // Load object values + $this->_load_values($result->current()); + } + else + { + // Clear the object, nothing was found + $this->clear(); + } + + return $this; + } + } + + /** + * Loads an array of values into into the current object. + * + * @chainable + * @param array $values Values to load + * @return ORM + */ + protected function _load_values(array $values) + { + if (array_key_exists($this->_primary_key, $values)) + { + if ($values[$this->_primary_key] !== NULL) + { + // Flag as loaded and valid + $this->_loaded = $this->_valid = TRUE; + + // Store primary key + $this->_primary_key_value = $values[$this->_primary_key]; + } + else + { + // Not loaded or valid + $this->_loaded = $this->_valid = FALSE; + } + } + + // Related objects + $related = array(); + + foreach ($values as $column => $value) + { + if (strpos($column, ':') === FALSE) + { + // Load the value to this model + $this->_object[$column] = $value; + } + else + { + // Column belongs to a related model + list ($prefix, $column) = explode(':', $column, 2); + + $related[$prefix][$column] = $value; + } + } + + if ( ! empty($related)) + { + foreach ($related as $object => $values) + { + // Load the related objects with the values in the result + $this->_related($object)->_load_values($values); + } + } + + if ($this->_loaded) + { + // Store the object in its original state + $this->_original_values = $this->_object; + } + + return $this; + } + + /** + * Rule definitions for validation + * + * @return array + */ + public function rules() + { + return array(); + } + + /** + * Filters a value for a specific column + * + * @param string $field The column name + * @param string $value The value to filter + * @return string + */ + protected function run_filter($field, $value) + { + $filters = $this->filters(); + + // Get the filters for this column + $wildcards = empty($filters[TRUE]) ? array() : $filters[TRUE]; + + // Merge in the wildcards + $filters = empty($filters[$field]) ? $wildcards : array_merge($wildcards, $filters[$field]); + + // Bind the field name and model so they can be used in the filter method + $_bound = array + ( + ':field' => $field, + ':model' => $this, + ); + + foreach ($filters as $array) + { + // Value needs to be bound inside the loop so we are always using the + // version that was modified by the filters that already ran + $_bound[':value'] = $value; + + // Filters are defined as array($filter, $params) + $filter = $array[0]; + $params = Arr::get($array, 1, array(':value')); + + foreach ($params as $key => $param) + { + if (is_string($param) AND array_key_exists($param, $_bound)) + { + // Replace with bound value + $params[$key] = $_bound[$param]; + } + } + + if (is_array($filter) OR ! is_string($filter)) + { + // This is either a callback as an array or a lambda + $value = call_user_func_array($filter, $params); + } + elseif (strpos($filter, '::') === FALSE) + { + // Use a function call + $function = new ReflectionFunction($filter); + + // Call $function($this[$field], $param, ...) with Reflection + $value = $function->invokeArgs($params); + } + else + { + // Split the class and method of the rule + list($class, $method) = explode('::', $filter, 2); + + // Use a static method call + $method = new ReflectionMethod($class, $method); + + // Call $Class::$method($this[$field], $param, ...) with Reflection + $value = $method->invokeArgs(NULL, $params); + } + } + + return $value; + } + + /** + * Filter definitions for validation + * + * @return array + */ + public function filters() + { + return array(); + } + + /** + * Label definitions for validation + * + * @return array + */ + public function labels() + { + return array(); + } + + /** + * Validates the current model's data + * + * @param Validation $extra_validation Validation object + * @throws ORM_Validation_Exception + * @return ORM + */ + public function check(Validation $extra_validation = NULL) + { + // Determine if any external validation failed + $extra_errors = ($extra_validation AND ! $extra_validation->check()); + + // Always build a new validation object + $this->_validation(); + + $array = $this->_validation; + + if (($this->_valid = $array->check()) === FALSE OR $extra_errors) + { + $exception = new ORM_Validation_Exception($this->errors_filename(), $array); + + if ($extra_errors) + { + // Merge any possible errors from the external object + $exception->add_object('_external', $extra_validation); + } + throw $exception; + } + + return $this; + } + + /** + * Insert a new object to the database + * @param Validation $validation Validation object + * @throws Kohana_Exception + * @return ORM + */ + public function create(Validation $validation = NULL) + { + if ($this->_loaded) + throw new Kohana_Exception('Cannot create :model model because it is already loaded.', array(':model' => $this->_object_name)); + + // Require model validation before saving + if ( ! $this->_valid OR $validation) + { + $this->check($validation); + } + + $data = array(); + foreach ($this->_changed as $column) + { + // Generate list of column => values + $data[$column] = $this->_object[$column]; + } + + if (is_array($this->_created_column)) + { + // Fill the created column + $column = $this->_created_column['column']; + $format = $this->_created_column['format']; + + $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); + } + + $result = DB::insert($this->_table_name) + ->columns(array_keys($data)) + ->values(array_values($data)) + ->execute($this->_db); + + if ( ! array_key_exists($this->_primary_key, $data)) + { + // Load the insert id as the primary key if it was left out + $this->_object[$this->_primary_key] = $this->_primary_key_value = $result[0]; + } + else + { + $this->_primary_key_value = $this->_object[$this->_primary_key]; + } + + // Object is now loaded and saved + $this->_loaded = $this->_saved = TRUE; + + // All changes have been saved + $this->_changed = array(); + $this->_original_values = $this->_object; + + return $this; + } + + /** + * Updates a single record or multiple records + * + * @chainable + * @param Validation $validation Validation object + * @throws Kohana_Exception + * @return ORM + */ + public function update(Validation $validation = NULL) + { + if ( ! $this->_loaded) + throw new Kohana_Exception('Cannot update :model model because it is not loaded.', array(':model' => $this->_object_name)); + + // Run validation if the model isn't valid or we have additional validation rules. + if ( ! $this->_valid OR $validation) + { + $this->check($validation); + } + + if (empty($this->_changed)) + { + // Nothing to update + return $this; + } + + $data = array(); + foreach ($this->_changed as $column) + { + // Compile changed data + $data[$column] = $this->_object[$column]; + } + + if (is_array($this->_updated_column)) + { + // Fill the updated column + $column = $this->_updated_column['column']; + $format = $this->_updated_column['format']; + + $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); + } + + // Use primary key value + $id = $this->pk(); + + // Update a single record + DB::update($this->_table_name) + ->set($data) + ->where($this->_primary_key, '=', $id) + ->execute($this->_db); + + if (isset($data[$this->_primary_key])) + { + // Primary key was changed, reflect it + $this->_primary_key_value = $data[$this->_primary_key]; + } + + // Object has been saved + $this->_saved = TRUE; + + // All changes have been saved + $this->_changed = array(); + $this->_original_values = $this->_object; + + return $this; + } + + /** + * Updates or Creates the record depending on loaded() + * + * @chainable + * @param Validation $validation Validation object + * @return ORM + */ + public function save(Validation $validation = NULL) + { + return $this->loaded() ? $this->update($validation) : $this->create($validation); + } + + /** + * Deletes a single record while ignoring relationships. + * + * @chainable + * @throws Kohana_Exception + * @return ORM + */ + public function delete() + { + if ( ! $this->_loaded) + throw new Kohana_Exception('Cannot delete :model model because it is not loaded.', array(':model' => $this->_object_name)); + + // Use primary key value + $id = $this->pk(); + + // Delete the object + DB::delete($this->_table_name) + ->where($this->_primary_key, '=', $id) + ->execute($this->_db); + + return $this->clear(); + } + + /** + * Tests if this object has a relationship to a different model, + * or an array of different models. When providing far keys, the number + * of relations must equal the number of keys. + * + * + * // Check if $model has the login role + * $model->has('roles', ORM::factory('role', array('name' => 'login'))); + * // Check for the login role if you know the roles.id is 5 + * $model->has('roles', 5); + * // Check for all of the following roles + * $model->has('roles', array(1, 2, 3, 4)); + * // Check if $model has any roles + * $model->has('roles') + * + * @param string $alias Alias of the has_many "through" relationship + * @param mixed $far_keys Related model, primary key, or an array of primary keys + * @return boolean + */ + public function has($alias, $far_keys = NULL) + { + $count = $this->count_relations($alias, $far_keys); + if ($far_keys === NULL) + { + return (bool) $count; + } + else + { + return $count === count($far_keys); + } + + } + + /** + * Tests if this object has a relationship to a different model, + * or an array of different models. When providing far keys, this function + * only checks that at least one of the relationships is satisfied. + * + * // Check if $model has the login role + * $model->has('roles', ORM::factory('role', array('name' => 'login'))); + * // Check for the login role if you know the roles.id is 5 + * $model->has('roles', 5); + * // Check for any of the following roles + * $model->has('roles', array(1, 2, 3, 4)); + * // Check if $model has any roles + * $model->has('roles') + * + * @param string $alias Alias of the has_many "through" relationship + * @param mixed $far_keys Related model, primary key, or an array of primary keys + * @return boolean + */ + public function has_any($alias, $far_keys = NULL) + { + return (bool) $this->count_relations($alias, $far_keys); + } + + /** + * Returns the number of relationships + * + * // Counts the number of times the login role is attached to $model + * $model->has('roles', ORM::factory('role', array('name' => 'login'))); + * // Counts the number of times role 5 is attached to $model + * $model->has('roles', 5); + * // Counts the number of times any of roles 1, 2, 3, or 4 are attached to + * // $model + * $model->has('roles', array(1, 2, 3, 4)); + * // Counts the number roles attached to $model + * $model->has('roles') + * + * @param string $alias Alias of the has_many "through" relationship + * @param mixed $far_keys Related model, primary key, or an array of primary keys + * @return integer + */ + public function count_relations($alias, $far_keys = NULL) + { + if ($far_keys === NULL) + { + return (int) DB::select(array(DB::expr('COUNT(*)'), 'records_found')) + ->from($this->_has_many[$alias]['through']) + ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk()) + ->execute($this->_db)->get('records_found'); + } + + $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys; + + // We need an array to simplify the logic + $far_keys = (array) $far_keys; + + // Nothing to check if the model isn't loaded or we don't have any far_keys + if ( ! $far_keys OR ! $this->_loaded) + return 0; + + $count = (int) DB::select(array(DB::expr('COUNT(*)'), 'records_found')) + ->from($this->_has_many[$alias]['through']) + ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk()) + ->where($this->_has_many[$alias]['far_key'], 'IN', $far_keys) + ->execute($this->_db)->get('records_found'); + + // Rows found need to match the rows searched + return (int) $count; + } + + /** + * Adds a new relationship to between this model and another. + * + * // Add the login role using a model instance + * $model->add('roles', ORM::factory('role', array('name' => 'login'))); + * // Add the login role if you know the roles.id is 5 + * $model->add('roles', 5); + * // Add multiple roles (for example, from checkboxes on a form) + * $model->add('roles', array(1, 2, 3, 4)); + * + * @param string $alias Alias of the has_many "through" relationship + * @param mixed $far_keys Related model, primary key, or an array of primary keys + * @return ORM + */ + public function add($alias, $far_keys) + { + $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys; + + $columns = array($this->_has_many[$alias]['foreign_key'], $this->_has_many[$alias]['far_key']); + $foreign_key = $this->pk(); + + $query = DB::insert($this->_has_many[$alias]['through'], $columns); + + foreach ( (array) $far_keys as $key) + { + $query->values(array($foreign_key, $key)); + } + + $query->execute($this->_db); + + return $this; + } + + /** + * Removes a relationship between this model and another. + * + * // Remove a role using a model instance + * $model->remove('roles', ORM::factory('role', array('name' => 'login'))); + * // Remove the role knowing the primary key + * $model->remove('roles', 5); + * // Remove multiple roles (for example, from checkboxes on a form) + * $model->remove('roles', array(1, 2, 3, 4)); + * // Remove all related roles + * $model->remove('roles'); + * + * @param string $alias Alias of the has_many "through" relationship + * @param mixed $far_keys Related model, primary key, or an array of primary keys + * @return ORM + */ + public function remove($alias, $far_keys = NULL) + { + $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys; + + $query = DB::delete($this->_has_many[$alias]['through']) + ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk()); + + if ($far_keys !== NULL) + { + // Remove all the relationships in the array + $query->where($this->_has_many[$alias]['far_key'], 'IN', (array) $far_keys); + } + + $query->execute($this->_db); + + return $this; + } + + /** + * Count the number of records in the table. + * + * @return integer + */ + public function count_all() + { + $selects = array(); + + foreach ($this->_db_pending as $key => $method) + { + if ($method['name'] == 'select') + { + // Ignore any selected columns for now + $selects[] = $method; + unset($this->_db_pending[$key]); + } + } + + if ( ! empty($this->_load_with)) + { + foreach ($this->_load_with as $alias) + { + // Bind relationship + $this->with($alias); + } + } + + $this->_build(Database::SELECT); + + $records = $this->_db_builder->from(array($this->_table_name, $this->_object_name)) + ->select(array(DB::expr('COUNT(*)'), 'records_found')) + ->execute($this->_db) + ->get('records_found'); + + // Add back in selected columns + $this->_db_pending += $selects; + + $this->reset(); + + // Return the total number of records in a table + return $records; + } + + /** + * Proxy method to Database list_columns. + * + * @return array + */ + public function list_columns() + { + // Proxy to database + return $this->_db->list_columns($this->_table_name); + } + + /** + * Returns an ORM model for the given one-one related alias + * + * @param string $alias Alias name + * @return ORM + */ + protected function _related($alias) + { + if (isset($this->_related[$alias])) + { + return $this->_related[$alias]; + } + elseif (isset($this->_has_one[$alias])) + { + return $this->_related[$alias] = ORM::factory($this->_has_one[$alias]['model']); + } + elseif (isset($this->_belongs_to[$alias])) + { + return $this->_related[$alias] = ORM::factory($this->_belongs_to[$alias]['model']); + } + else + { + return FALSE; + } + } + + /** + * Returns the value of the primary key + * + * @return mixed Primary key + */ + public function pk() + { + return $this->_primary_key_value; + } + + /** + * Returns last executed query + * + * @return string + */ + public function last_query() + { + return $this->_db->last_query; + } + + /** + * Clears query builder. Passing FALSE is useful to keep the existing + * query conditions for another query. + * + * @param bool $next Pass FALSE to avoid resetting on the next call + * @return ORM + */ + public function reset($next = TRUE) + { + if ($next AND $this->_db_reset) + { + $this->_db_pending = array(); + $this->_db_applied = array(); + $this->_db_builder = NULL; + $this->_with_applied = array(); + } + + // Reset on the next call? + $this->_db_reset = $next; + + return $this; + } + + protected function _serialize_value($value) + { + return json_encode($value); + } + + protected function _unserialize_value($value) + { + return json_decode($value, TRUE); + } + + public function object_name() + { + return $this->_object_name; + } + + public function object_plural() + { + return $this->_object_plural; + } + + public function loaded() + { + return $this->_loaded; + } + + public function saved() + { + return $this->_saved; + } + + public function primary_key() + { + return $this->_primary_key; + } + + public function table_name() + { + return $this->_table_name; + } + + public function table_columns() + { + return $this->_table_columns; + } + + public function has_one() + { + return $this->_has_one; + } + + public function belongs_to() + { + return $this->_belongs_to; + } + + public function has_many() + { + return $this->_has_many; + } + + public function load_with() + { + return $this->_load_with; + } + + public function original_values() + { + return $this->_original_values; + } + + public function created_column() + { + return $this->_created_column; + } + + public function updated_column() + { + return $this->_updated_column; + } + + public function validation() + { + if ( ! isset($this->_validation)) + { + // Initialize the validation object + $this->_validation(); + } + + return $this->_validation; + } + + public function object() + { + return $this->_object; + } + + public function errors_filename() + { + return $this->_errors_filename; + } + + /** + * Alias of and_where() + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function where($column, $op, $value) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'where', + 'args' => array($column, $op, $value), + ); + + return $this; + } + + /** + * Creates a new "AND WHERE" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function and_where($column, $op, $value) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'and_where', + 'args' => array($column, $op, $value), + ); + + return $this; + } + + /** + * Creates a new "OR WHERE" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function or_where($column, $op, $value) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'or_where', + 'args' => array($column, $op, $value), + ); + + return $this; + } + + /** + * Alias of and_where_open() + * + * @return $this + */ + public function where_open() + { + return $this->and_where_open(); + } + + /** + * Opens a new "AND WHERE (...)" grouping. + * + * @return $this + */ + public function and_where_open() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'and_where_open', + 'args' => array(), + ); + + return $this; + } + + /** + * Opens a new "OR WHERE (...)" grouping. + * + * @return $this + */ + public function or_where_open() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'or_where_open', + 'args' => array(), + ); + + return $this; + } + + /** + * Closes an open "AND WHERE (...)" grouping. + * + * @return $this + */ + public function where_close() + { + return $this->and_where_close(); + } + + /** + * Closes an open "AND WHERE (...)" grouping. + * + * @return $this + */ + public function and_where_close() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'and_where_close', + 'args' => array(), + ); + + return $this; + } + + /** + * Closes an open "OR WHERE (...)" grouping. + * + * @return $this + */ + public function or_where_close() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'or_where_close', + 'args' => array(), + ); + + return $this; + } + + /** + * Applies sorting with "ORDER BY ..." + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $direction direction of sorting + * @return $this + */ + public function order_by($column, $direction = NULL) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'order_by', + 'args' => array($column, $direction), + ); + + return $this; + } + + /** + * Return up to "LIMIT ..." results + * + * @param integer $number maximum results to return + * @return $this + */ + public function limit($number) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'limit', + 'args' => array($number), + ); + + return $this; + } + + /** + * Enables or disables selecting only unique columns using "SELECT DISTINCT" + * + * @param boolean $value enable or disable distinct columns + * @return $this + */ + public function distinct($value) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'distinct', + 'args' => array($value), + ); + + return $this; + } + + /** + * Choose the columns to select from. + * + * @param mixed $columns column name or array($column, $alias) or object + * @param ... + * @return $this + */ + public function select($columns = NULL) + { + $columns = func_get_args(); + + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'select', + 'args' => $columns, + ); + + return $this; + } + + /** + * Choose the tables to select "FROM ..." + * + * @param mixed $tables table name or array($table, $alias) or object + * @param ... + * @return $this + */ + public function from($tables) + { + $tables = func_get_args(); + + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'from', + 'args' => $tables, + ); + + return $this; + } + + /** + * Adds addition tables to "JOIN ...". + * + * @param mixed $table column name or array($column, $alias) or object + * @param string $type join type (LEFT, RIGHT, INNER, etc) + * @return $this + */ + public function join($table, $type = NULL) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'join', + 'args' => array($table, $type), + ); + + return $this; + } + + /** + * Adds "ON ..." conditions for the last created JOIN statement. + * + * @param mixed $c1 column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $c2 column name or array($column, $alias) or object + * @return $this + */ + public function on($c1, $op, $c2) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'on', + 'args' => array($c1, $op, $c2), + ); + + return $this; + } + + /** + * Creates a "GROUP BY ..." filter. + * + * @param mixed $columns column name or array($column, $alias) or object + * @param ... + * @return $this + */ + public function group_by($columns) + { + $columns = func_get_args(); + + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'group_by', + 'args' => $columns, + ); + + return $this; + } + + /** + * Alias of and_having() + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function having($column, $op, $value = NULL) + { + return $this->and_having($column, $op, $value); + } + + /** + * Creates a new "AND HAVING" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function and_having($column, $op, $value = NULL) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'and_having', + 'args' => array($column, $op, $value), + ); + + return $this; + } + + /** + * Creates a new "OR HAVING" condition for the query. + * + * @param mixed $column column name or array($column, $alias) or object + * @param string $op logic operator + * @param mixed $value column value + * @return $this + */ + public function or_having($column, $op, $value = NULL) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'or_having', + 'args' => array($column, $op, $value), + ); + + return $this; + } + + /** + * Alias of and_having_open() + * + * @return $this + */ + public function having_open() + { + return $this->and_having_open(); + } + + /** + * Opens a new "AND HAVING (...)" grouping. + * + * @return $this + */ + public function and_having_open() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'and_having_open', + 'args' => array(), + ); + + return $this; + } + + /** + * Opens a new "OR HAVING (...)" grouping. + * + * @return $this + */ + public function or_having_open() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'or_having_open', + 'args' => array(), + ); + + return $this; + } + + /** + * Closes an open "AND HAVING (...)" grouping. + * + * @return $this + */ + public function having_close() + { + return $this->and_having_close(); + } + + /** + * Closes an open "AND HAVING (...)" grouping. + * + * @return $this + */ + public function and_having_close() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'and_having_close', + 'args' => array(), + ); + + return $this; + } + + /** + * Closes an open "OR HAVING (...)" grouping. + * + * @return $this + */ + public function or_having_close() + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'or_having_close', + 'args' => array(), + ); + + return $this; + } + + /** + * Start returning results after "OFFSET ..." + * + * @param integer $number starting result number + * @return $this + */ + public function offset($number) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'offset', + 'args' => array($number), + ); + + return $this; + } + + /** + * Enables the query to be cached for a specified amount of time. + * + * @param integer $lifetime number of seconds to cache + * @return $this + * @uses Kohana::$cache_life + */ + public function cached($lifetime = NULL) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'cached', + 'args' => array($lifetime), + ); + + return $this; + } + + /** + * Set the value of a parameter in the query. + * + * @param string $param parameter key to replace + * @param mixed $value value to use + * @return $this + */ + public function param($param, $value) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'param', + 'args' => array($param, $value), + ); + + return $this; + } + + /** + * Adds "USING ..." conditions for the last created JOIN statement. + * + * @param string $columns column name + * @return $this + */ + public function using($columns) + { + // Add pending database call which is executed after query type is determined + $this->_db_pending[] = array( + 'name' => 'using', + 'args' => array($columns), + ); + + return $this; + } + + /** + * Checks whether a column value is unique. + * Excludes itself if loaded. + * + * @param string $field the field to check for uniqueness + * @param mixed $value the value to check for uniqueness + * @return bool whteher the value is unique + */ + public function unique($field, $value) + { + $model = ORM::factory($this->object_name()) + ->where($field, '=', $value) + ->find(); + + if ($this->loaded()) + { + return ( ! ($model->loaded() AND $model->pk() != $this->pk())); + } + + return ( ! $model->loaded()); + } +} // End ORM diff --git a/modules/orm/classes/Kohana/ORM/Validation/Exception.php b/modules/orm/classes/Kohana/ORM/Validation/Exception.php new file mode 100644 index 0000000..d88fcd1 --- /dev/null +++ b/modules/orm/classes/Kohana/ORM/Validation/Exception.php @@ -0,0 +1,198 @@ +_alias = $alias; + $this->_objects['_object'] = $object; + $this->_objects['_has_many'] = FALSE; + + parent::__construct($message, $values, $code, $previous); + } + + /** + * Adds a Validation object to this exception + * + * // The following will add a validation object for a profile model + * // inside the exception for a user model. + * $e->add_object('profile', $validation); + * // The errors array will now look something like this + * // array + * // ( + * // 'username' => 'This field is required', + * // 'profile' => array + * // ( + * // 'first_name' => 'This field is required', + * // ), + * // ); + * + * @param string $alias The relationship alias from the model + * @param Validation $object The Validation object to merge + * @param mixed $has_many The array key to use if this exception can be merged multiple times + * @return ORM_Validation_Exception + */ + public function add_object($alias, Validation $object, $has_many = FALSE) + { + // We will need this when generating errors + $this->_objects[$alias]['_has_many'] = ($has_many !== FALSE); + + if ($has_many === TRUE) + { + // This is most likely a has_many relationship + $this->_objects[$alias][]['_object'] = $object; + } + elseif ($has_many) + { + // This is most likely a has_many relationship + $this->_objects[$alias][$has_many]['_object'] = $object; + } + else + { + $this->_objects[$alias]['_object'] = $object; + } + + return $this; + } + + /** + * Merges an ORM_Validation_Exception object into the current exception + * Useful when you want to combine errors into one array + * + * @param ORM_Validation_Exception $object The exception to merge + * @param mixed $has_many The array key to use if this exception can be merged multiple times + * @return ORM_Validation_Exception + */ + public function merge(ORM_Validation_Exception $object, $has_many = FALSE) + { + $alias = $object->alias(); + + // We will need this when generating errors + $this->_objects[$alias]['_has_many'] = ($has_many !== FALSE); + + if ($has_many === TRUE) + { + // This is most likely a has_many relationship + $this->_objects[$alias][] = $object->objects(); + } + elseif ($has_many) + { + // This is most likely a has_many relationship + $this->_objects[$alias][$has_many] = $object->objects(); + } + else + { + $this->_objects[$alias] = $object->objects(); + } + + return $this; + } + + /** + * Returns a merged array of the errors from all the Validation objects in this exception + * + * // Will load Model_User errors from messages/orm-validation/user.php + * $e->errors('orm-validation'); + * + * @param string $directory Directory to load error messages from + * @param mixed $translate Translate the message + * @return array + * @see generate_errors() + */ + public function errors($directory = NULL, $translate = TRUE) + { + return $this->generate_errors($this->_alias, $this->_objects, $directory, $translate); + } + + /** + * Recursive method to fetch all the errors in this exception + * + * @param string $alias Alias to use for messages file + * @param array $array Array of Validation objects to get errors from + * @param string $directory Directory to load error messages from + * @param mixed $translate Translate the message + * @return array + */ + protected function generate_errors($alias, array $array, $directory, $translate) + { + $errors = array(); + + foreach ($array as $key => $object) + { + if (is_array($object)) + { + $errors[$key] = ($key === '_external') + // Search for errors in $alias/_external.php + ? $this->generate_errors($alias.'/'.$key, $object, $directory, $translate) + // Regular models get their own file not nested within $alias + : $this->generate_errors($key, $object, $directory, $translate); + } + elseif ($object instanceof Validation) + { + if ($directory === NULL) + { + // Return the raw errors + $file = NULL; + } + else + { + $file = trim($directory.'/'.$alias, '/'); + } + + // Merge in this array of errors + $errors += $object->errors($file, $translate); + } + } + + return $errors; + } + + /** + * Returns the protected _objects property from this exception + * + * @return array + */ + public function objects() + { + return $this->_objects; + } + + /** + * Returns the protected _alias property from this exception + * + * @return string + */ + public function alias() + { + return $this->_alias; + } +} // End Kohana_ORM_Validation_Exception diff --git a/modules/orm/classes/Model/Auth/Role.php b/modules/orm/classes/Model/Auth/Role.php new file mode 100644 index 0000000..5a12764 --- /dev/null +++ b/modules/orm/classes/Model/Auth/Role.php @@ -0,0 +1,31 @@ + array('model' => 'User','through' => 'roles_users'), + ); + + public function rules() + { + return array( + 'name' => array( + array('not_empty'), + array('min_length', array(':value', 4)), + array('max_length', array(':value', 32)), + ), + 'description' => array( + array('max_length', array(':value', 255)), + ) + ); + } + +} // End Auth Role Model diff --git a/modules/orm/classes/Model/Auth/User.php b/modules/orm/classes/Model/Auth/User.php new file mode 100644 index 0000000..d86fae4 --- /dev/null +++ b/modules/orm/classes/Model/Auth/User.php @@ -0,0 +1,204 @@ + array('model' => 'User_Token'), + 'roles' => array('model' => 'Role', 'through' => 'roles_users'), + ); + + /** + * Rules for the user model. Because the password is _always_ a hash + * when it's set,you need to run an additional not_empty rule in your controller + * to make sure you didn't hash an empty string. The password rules + * should be enforced outside the model or with a model helper method. + * + * @return array Rules + */ + public function rules() + { + return array( + 'username' => array( + array('not_empty'), + array('max_length', array(':value', 32)), + array(array($this, 'unique'), array('username', ':value')), + ), + 'password' => array( + array('not_empty'), + ), + 'email' => array( + array('not_empty'), + array('email'), + array(array($this, 'unique'), array('email', ':value')), + ), + ); + } + + /** + * Filters to run when data is set in this model. The password filter + * automatically hashes the password when it's set in the model. + * + * @return array Filters + */ + public function filters() + { + return array( + 'password' => array( + array(array(Auth::instance(), 'hash')) + ) + ); + } + + /** + * Labels for fields in this model + * + * @return array Labels + */ + public function labels() + { + return array( + 'username' => 'username', + 'email' => 'email address', + 'password' => 'password', + ); + } + + /** + * Complete the login for a user by incrementing the logins and saving login timestamp + * + * @return void + */ + public function complete_login() + { + if ($this->_loaded) + { + // Update the number of logins + $this->logins = new Database_Expression('logins + 1'); + + // Set the last login date + $this->last_login = time(); + + // Save the user + $this->update(); + } + } + + /** + * Tests if a unique key value exists in the database. + * + * @param mixed the value to test + * @param string field name + * @return boolean + */ + public function unique_key_exists($value, $field = NULL) + { + if ($field === NULL) + { + // Automatically determine field by looking at the value + $field = $this->unique_key($value); + } + + return (bool) DB::select(array(DB::expr('COUNT(*)'), 'total_count')) + ->from($this->_table_name) + ->where($field, '=', $value) + ->where($this->_primary_key, '!=', $this->pk()) + ->execute($this->_db) + ->get('total_count'); + } + + /** + * Allows a model use both email and username as unique identifiers for login + * + * @param string unique value + * @return string field name + */ + public function unique_key($value) + { + return Valid::email($value) ? 'email' : 'username'; + } + + /** + * Password validation for plain passwords. + * + * @param array $values + * @return Validation + */ + public static function get_password_validation($values) + { + return Validation::factory($values) + ->rule('password', 'min_length', array(':value', 8)) + ->rule('password_confirm', 'matches', array(':validation', ':field', 'password')); + } + + /** + * Create a new user + * + * Example usage: + * ~~~ + * $user = ORM::factory('User')->create_user($_POST, array( + * 'username', + * 'password', + * 'email', + * ); + * ~~~ + * + * @param array $values + * @param array $expected + * @throws ORM_Validation_Exception + */ + public function create_user($values, $expected) + { + // Validation for passwords + $extra_validation = Model_User::get_password_validation($values) + ->rule('password', 'not_empty'); + + return $this->values($values, $expected)->create($extra_validation); + } + + /** + * Update an existing user + * + * [!!] We make the assumption that if a user does not supply a password, that they do not wish to update their password. + * + * Example usage: + * ~~~ + * $user = ORM::factory('User') + * ->where('username', '=', 'kiall') + * ->find() + * ->update_user($_POST, array( + * 'username', + * 'password', + * 'email', + * ); + * ~~~ + * + * @param array $values + * @param array $expected + * @throws ORM_Validation_Exception + */ + public function update_user($values, $expected = NULL) + { + if (empty($values['password'])) + { + unset($values['password'], $values['password_confirm']); + } + + // Validation for passwords + $extra_validation = Model_User::get_password_validation($values); + + return $this->values($values, $expected)->update($extra_validation); + } + +} // End Auth User Model diff --git a/modules/orm/classes/Model/Auth/User/Token.php b/modules/orm/classes/Model/Auth/User/Token.php new file mode 100644 index 0000000..3c69ead --- /dev/null +++ b/modules/orm/classes/Model/Auth/User/Token.php @@ -0,0 +1,77 @@ + array('model' => 'User'), + ); + + protected $_created_column = array( + 'column' => 'created', + 'format' => TRUE, + ); + + /** + * Handles garbage collection and deleting of expired objects. + * + * @return void + */ + public function __construct($id = NULL) + { + parent::__construct($id); + + if (mt_rand(1, 100) === 1) + { + // Do garbage collection + $this->delete_expired(); + } + + if ($this->expires < time() AND $this->_loaded) + { + // This object has expired + $this->delete(); + } + } + + /** + * Deletes all expired tokens. + * + * @return ORM + */ + public function delete_expired() + { + // Delete all expired tokens + DB::delete($this->_table_name) + ->where('expires', '<', time()) + ->execute($this->_db); + + return $this; + } + + public function create(Validation $validation = NULL) + { + $this->token = $this->create_token(); + + return parent::create($validation); + } + + protected function create_token() + { + do + { + $token = sha1(uniqid(Text::random('alnum', 32), TRUE)); + } + while (ORM::factory('User_Token', array('token' => $token))->loaded()); + + return $token; + } + +} // End Auth User Token Model diff --git a/modules/orm/classes/Model/Role.php b/modules/orm/classes/Model/Role.php new file mode 100644 index 0000000..b1ead06 --- /dev/null +++ b/modules/orm/classes/Model/Role.php @@ -0,0 +1,7 @@ + array( + + // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' + 'orm' => array( + + // Whether this modules userguide pages should be shown + 'enabled' => TRUE, + + // The name that should show up on the userguide index page + 'name' => 'ORM', + + // A short description of this module, shown on the index page + 'description' => 'Official ORM module, a modeling library for object relational mapping.', + + // Copyright message, shown in the footer for this module + 'copyright' => '© 2008–2012 Kohana Team', + ) + ) +); diff --git a/modules/orm/guide/orm/examples.md b/modules/orm/guide/orm/examples.md new file mode 100644 index 0000000..5c21dd4 --- /dev/null +++ b/modules/orm/guide/orm/examples.md @@ -0,0 +1,13 @@ +# Examples + +- [Simple](examples/simple): Basic, one table model examples. +- [Validation](examples/validation): Full example of creating a user account and handling validation errors. + +## @TODO: + +The following is a sample list of examples that might be useful. Don't feel limited by this list, or consider these required. Items on the list can be combined, split up, removed or added to. All contribution are appreciated. + +- Examples of changing things like $_table_name, $_labels, with, etc. +- Example of a one to one relationship. +- Example of one to many +- Example of many to many. diff --git a/modules/orm/guide/orm/examples/simple.md b/modules/orm/guide/orm/examples/simple.md new file mode 100644 index 0000000..b8abe92 --- /dev/null +++ b/modules/orm/guide/orm/examples/simple.md @@ -0,0 +1,119 @@ +# Simple Examples + +This is a simple example of a single ORM model, that has no relationships, but uses validation on the fields. + +## SQL schema + + CREATE TABLE IF NOT EXISTS `members` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(32) NOT NULL, + `first_name` varchar(32) NOT NULL, + `last_name` varchar(32) NOT NULL, + `email` varchar(127) DEFAULT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + +## Model + + array( + array('not_empty'), + array('min_length', array(':value', 4)), + array('max_length', array(':value', 32)), + array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), + ), + 'first_name' => array( + array('not_empty'), + array('min_length', array(':value', 4)), + array('max_length', array(':value', 32)), + array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), + ), + 'last_name' => array( + array('not_empty'), + array('min_length', array(':value', 4)), + array('max_length', array(':value', 32)), + array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), + ), + 'email' => array( + array('not_empty'), + array('min_length', array(':value', 4)), + array('max_length', array(':value', 127)), + array('email'), + ), + ); + } + } + +[!!] The array returned by `ORM::rules()` will be passed to a [Validation] object and tested when you call `ORM::save()`. + +[!!] Please notice that defining the primary key "id" in the model is not necessary. Also the table name in the database is plural and the model name is singular. + +## Controller + + where('first_name', '=', 'Peter')->find_all(); + + // Count records in the $members object + $members->count_all(); + + /** + * Example 2 + */ + + // Create an instance of a model + $member = ORM::factory('Member'); + + // Get a member with the user name "bongo" find() means + // we only want the first record matching the query. + $member->where('username', '=', 'bongo')->find(); + + /** + * Example 3 + */ + + // Create an instance of a model + $member = ORM::factory('Member'); + + // Do an INSERT query + $member->username = 'bongo'; + $member->first_name = 'Peter'; + $member->last_name = 'Smith'; + $member->save(); + + /** + * Example 4 + */ + + // Create an instance of a model where the + // table field "id" is "1" + $member = ORM::factory('Member', 1); + + // Do an UPDATE query + $member->username = 'bongo'; + $member->first_name = 'Peter'; + $member->last_name = 'Smith'; + $member->save(); + } + } + +[!!] $member will be a PHP object where you can access the values from the query e.g. echo $member->first_name diff --git a/modules/orm/guide/orm/examples/validation.md b/modules/orm/guide/orm/examples/validation.md new file mode 100644 index 0000000..0510a45 --- /dev/null +++ b/modules/orm/guide/orm/examples/validation.md @@ -0,0 +1,137 @@ +# Validation Example + +This example will create user accounts and demonstrate how to handle model and controller validation. We will create a form, process it, and display any errors to the user. We will be assuming that the Model_User class contains a method called `hash_password` that is used to turn the plaintext passwords into some kind of hash. The implementation of the hashing methods are beyond the scope of this example and should be provided with the Authentication library you decide to use. + +## SQL schema + + CREATE TABLE IF NOT EXISTS `members` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(32) NOT NULL, + `password` varchar(100) NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + +## Model + + array( + array('not_empty'), + array('min_length', array(':value', 4)), + array('max_length', array(':value', 32)), + array(array($this, 'username_available')), + ), + 'password' => array( + array('not_empty'), + ), + ); + } + + public function filters() + { + return array( + 'password' => array( + array(array($this, 'hash_password')), + ), + ); + } + + public function username_available($username) + { + // There are simpler ways to do this, but I will use ORM for the sake of the example + return ORM::factory('Member', array('username' => $username))->loaded(); + } + + public function hash_password($password) + { + // Do something to hash the password + } + } + +## HTML Form + +Please forgive my slightly ugly form. I am trying not to use any modules or unrelated magic. :) + +
    + + +
    a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), +k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, +scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= +false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= +1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
    ";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
    t
    ";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= +"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= +c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); +else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; +if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, +attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& +b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; +c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, +arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= +d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ +c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== +8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== +"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ +d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= +B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== +"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== +0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); +(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; +break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, +q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= +l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, +m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== +true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== +g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- +0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== +i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; +if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, +g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); +n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& +function(){var g=k,i=t.createElement("div");i.innerHTML="

    ";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| +p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= +t.createElement("div");g.innerHTML="
    ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? +function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= +h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): +c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, +2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, +b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& +e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, +""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
    ","
    "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; +else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", +prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- +1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); +d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, +jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, +zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), +h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); +if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= +d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; +e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, +ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== +"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
    ").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; +A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= +encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", +[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), +e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); +if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", +3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} +var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/modules/userguide/media/guide/js/kodoc.js b/modules/userguide/media/guide/js/kodoc.js new file mode 100644 index 0000000..597ebf9 --- /dev/null +++ b/modules/userguide/media/guide/js/kodoc.js @@ -0,0 +1,97 @@ +$(document).ready(function() +{ + +// Syntax highlighter + + $('pre:not(.debug) code').each(function() + { + $(this).addClass('brush: php, class-name: highlighted'); + }); + + SyntaxHighlighter.config.tagName = 'code'; + // Don't show the toolbar or line-numbers. + SyntaxHighlighter.defaults.gutter = false; + SyntaxHighlighter.all(); + + // Any link that has the current page as its href should be class="current" + $('a[href="'+ window.location.pathname +'"]').addClass('current'); + + // Breadcrumbs magic + $('#kodoc-breadcrumb li.last').each(function() + { + var $this = $(this); + var $topics = $('#kodoc-topics li').has('a.current').slice(0, -1); + + $topics.each(function() + { + // Create a copy of the menu link + var $crumb = $(this).children('a:first, span:not(.toggle):first').clone(); + + // Insert the menu link into the breadcrumbs + $('
  • ').html($crumb).insertBefore($this); + }); + }); + + // Collapsing menus + $('#kodoc-topics li:has(li)').each(function() + { + var $this = $(this); + var toggle = $(''); + var menu = $this.find('>ul,>ol'); + + toggle.click(function() + { + if (menu.is(':visible')) + { + menu.stop(true, true).slideUp('fast'); + toggle.html('+'); + } + else + { + menu.stop(true, true).slideDown('fast'); + toggle.html('–'); + } + }); + + $this.find('>span').click(function() + { + // Menu without a link + toggle.click(); + }); + + if ( ! $this.is(':has(a.current)')) + { + menu.hide(); + } + + toggle.html(menu.is(':visible') ? '–' : '+').prependTo($this); + }); + +// Show source links + + $('#kodoc-main .method-source').each(function() + { + var self = $(this); + var togg = $(' [show]').appendTo($('h4', self)); + var code = self.find('pre').hide(); + + togg.toggle(function() + { + togg.html('[hide]'); + code.stop(true, true).slideDown(); + }, + function() + { + togg.html('[show]'); + code.stop(true, true).slideUp(); + }); + }); + + // "Link to this" link that appears when you hover over a header + $('#kodoc-body') + .find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]') + .append(function(){ + var $this = $(this); + return ''; + }); +}); diff --git a/modules/userguide/media/guide/js/shBrushPhp.js b/modules/userguide/media/guide/js/shBrushPhp.js new file mode 100644 index 0000000..95e6e43 --- /dev/null +++ b/modules/userguide/media/guide/js/shBrushPhp.js @@ -0,0 +1,88 @@ +/** + * SyntaxHighlighter + * http://alexgorbatchev.com/SyntaxHighlighter + * + * SyntaxHighlighter is donationware. If you are using it, please donate. + * http://alexgorbatchev.com/SyntaxHighlighter/donate.html + * + * @version + * 3.0.83 (July 02 2010) + * + * @copyright + * Copyright (C) 2004-2010 Alex Gorbatchev. + * + * @license + * Dual licensed under the MIT and GPL licenses. + */ +;(function() +{ + // CommonJS + typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; + + function Brush() + { + var funcs = 'abs acos acosh addcslashes addslashes ' + + 'array_change_key_case array_chunk array_combine array_count_values array_diff '+ + 'array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_fill '+ + 'array_filter array_flip array_intersect array_intersect_assoc array_intersect_key '+ + 'array_intersect_uassoc array_intersect_ukey array_key_exists array_keys array_map '+ + 'array_merge array_merge_recursive array_multisort array_pad array_pop array_product '+ + 'array_push array_rand array_reduce array_reverse array_search array_shift '+ + 'array_slice array_splice array_sum array_udiff array_udiff_assoc '+ + 'array_udiff_uassoc array_uintersect array_uintersect_assoc '+ + 'array_uintersect_uassoc array_unique array_unshift array_values array_walk '+ + 'array_walk_recursive atan atan2 atanh base64_decode base64_encode base_convert '+ + 'basename bcadd bccomp bcdiv bcmod bcmul bindec bindtextdomain bzclose bzcompress '+ + 'bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite ceil chdir '+ + 'checkdate checkdnsrr chgrp chmod chop chown chr chroot chunk_split class_exists '+ + 'closedir closelog copy cos cosh count count_chars date decbin dechex decoct '+ + 'deg2rad delete ebcdic2ascii echo empty end ereg ereg_replace eregi eregi_replace error_log '+ + 'error_reporting escapeshellarg escapeshellcmd eval exec exit exp explode extension_loaded '+ + 'feof fflush fgetc fgetcsv fgets fgetss file_exists file_get_contents file_put_contents '+ + 'fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype '+ + 'floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv fputs fread fscanf '+ + 'fseek fsockopen fstat ftell ftok getallheaders getcwd getdate getenv gethostbyaddr gethostbyname '+ + 'gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid getmyuid getopt '+ + 'getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext '+ + 'gettimeofday gettype glob gmdate gmmktime ini_alter ini_get ini_get_all ini_restore ini_set '+ + 'interface_exists intval ip2long is_a is_array is_bool is_callable is_dir is_double '+ + 'is_executable is_file is_finite is_float is_infinite is_int is_integer is_link is_long '+ + 'is_nan is_null is_numeric is_object is_readable is_real is_resource is_scalar is_soap_fault '+ + 'is_string is_subclass_of is_uploaded_file is_writable is_writeable mkdir mktime nl2br '+ + 'parse_ini_file parse_str parse_url passthru pathinfo print readlink realpath rewind rewinddir rmdir '+ + 'round str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split '+ + 'str_word_count strcasecmp strchr strcmp strcoll strcspn strftime strip_tags stripcslashes '+ + 'stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpbrk '+ + 'strpos strptime strrchr strrev strripos strrpos strspn strstr strtok strtolower strtotime '+ + 'strtoupper strtr strval substr substr_compare'; + + var keywords = 'abstract and array as break case catch cfunction class clone const continue declare default die do ' + + 'else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach ' + + 'function include include_once global goto if implements interface instanceof namespace new ' + + 'old_function or private protected public return require require_once static switch ' + + 'throw try use var while xor '; + + var constants = '__FILE__ __LINE__ __METHOD__ __FUNCTION__ __CLASS__'; + + this.regexList = [ + { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments + { regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments + { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings + { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings + { regex: /\$\w+/g, css: 'variable' }, // variables + { regex: new RegExp(this.getKeywords(funcs), 'gmi'), css: 'functions' }, // common functions + { regex: new RegExp(this.getKeywords(constants), 'gmi'), css: 'constants' }, // constants + { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keyword + ]; + + this.forHtmlScript(SyntaxHighlighter.regexLib.phpScriptTags); + }; + + Brush.prototype = new SyntaxHighlighter.Highlighter(); + Brush.aliases = ['php']; + + SyntaxHighlighter.brushes.Php = Brush; + + // CommonJS + typeof(exports) != 'undefined' ? exports.Brush = Brush : null; +})(); diff --git a/modules/userguide/media/guide/js/shCore.js b/modules/userguide/media/guide/js/shCore.js new file mode 100644 index 0000000..b47b645 --- /dev/null +++ b/modules/userguide/media/guide/js/shCore.js @@ -0,0 +1,17 @@ +/** + * SyntaxHighlighter + * http://alexgorbatchev.com/SyntaxHighlighter + * + * SyntaxHighlighter is donationware. If you are using it, please donate. + * http://alexgorbatchev.com/SyntaxHighlighter/donate.html + * + * @version + * 3.0.83 (July 02 2010) + * + * @copyright + * Copyright (C) 2004-2010 Alex Gorbatchev. + * + * @license + * Dual licensed under the MIT and GPL licenses. + */ +eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a-1},3d:6(g){e+=g}};c1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;be.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;dd.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a\'+c+""});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.Pb.P)H 1;Y I(a.Lb.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'\'+c+""+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v<3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;">1v3v 3.0.76 (72 73 3x)1Z://3u.2w/1v70 17 6U 71.6T 6X-3x 6Y 6D.6t 61 60 J 1k, 5Z 5R 5V <2R/>5U 5T 5S!\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'\',d=e.16.2x,h=d.2X,g=0;g";H c},2o:6(a,b,c){H\'<2W>\'+c+""},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;md)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P\'+c+""},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i\'+j+"":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"":"")+\'<2d 1g="17">\'+b+""},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{})) diff --git a/modules/userguide/media/guide/js/sizzle.js b/modules/userguide/media/guide/js/sizzle.js new file mode 100644 index 0000000..c52b77d --- /dev/null +++ b/modules/userguide/media/guide/js/sizzle.js @@ -0,0 +1,1068 @@ +/*! + * Sizzle CSS Selector Engine - v1.0 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function(){ + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function(selector, context, results, seed) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context), + soFar = selector, ret, cur, pop, i; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec(""); + m = chunker.exec(soFar); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function(results){ + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } + + return results; +}; + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice(1,1); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context, isXML ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && Sizzle.isXML(set[0]); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var filter = Expr.filter[ type ], found, item, left = match[1]; + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + leftMatch: {}, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + attrHandle: { + href: function(elem){ + return elem.getAttribute("href"); + } + }, + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part){ + var isPartStr = typeof part === "string", + elem, i = 0, l = checkSet.length; + + if ( isPartStr && !/\W/.test(part) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck, nodeCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); + }, + "~": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck, nodeCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); + } + }, + find: { + ID: function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context){ + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not, isXML){ + match = " " + match[1].replace(/\\/g, "") + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + ID: function(match){ + return match[1].replace(/\\/g, ""); + }, + TAG: function(match, curLoop){ + return match[1].toLowerCase(); + }, + CHILD: function(match){ + if ( match[1] === "nth" ) { + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + ATTR: function(match, curLoop, inplace, result, not, isXML){ + var name = match[1].replace(/\\/g, ""); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + // Accessing this property makes selected-by-default + // options in Safari work properly + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return (/h\d/i).test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; + }, + input: function(elem){ + return (/input|select|textarea|button/i).test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 === i; + }, + eq: function(elem, i, match){ + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + } else { + Sizzle.error( "Syntax error, unrecognized expression: " + name ); + } + }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + if ( type === "first" ) { + return true; + } + node = elem; + case 'last': + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first === 0 ) { + return diff === 0; + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + CLASS: function(elem, match){ + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + ATTR: function(elem, match){ + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch(e){ + makeArray = function(array, results) { + var ret = results || [], i = 0; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.compareDocumentPosition ? -1 : 1; + } + + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + if ( !a.sourceIndex || !b.sourceIndex ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.sourceIndex ? -1 : 1; + } + + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + if ( !a.ownerDocument || !b.ownerDocument ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.ownerDocument ? -1 : 1; + } + + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.setStart(a, 0); + aRange.setEnd(a, 0); + bRange.setStart(b, 0); + bRange.setEnd(b, 0); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(); + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + root = form = null; // release memory in IE +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + Expr.attrHandle.href = function(elem){ + return elem.getAttribute("href", 2); + }; + } + + div = null; // release memory in IE +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, div = document.createElement("div"); + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; // release memory in IE + })(); +} + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; // release memory in IE +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +Sizzle.contains = document.compareDocumentPosition ? function(a, b){ + return !!(a.compareDocumentPosition(b) & 16); +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); +}; + +Sizzle.isXML = function(elem){ + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function(selector, context){ + var tmpSet = [], later = "", match, + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE + +window.Sizzle = Sizzle; + +})(); diff --git a/modules/userguide/media/guide/userguide/contrib-github-edit.png b/modules/userguide/media/guide/userguide/contrib-github-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..b90eb91a2994f4289f85899fba6f70a9ced1b671 GIT binary patch literal 9681 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 literal 0 HcmV?d00001 diff --git a/modules/userguide/media/guide/userguide/contrib-github-fork.png b/modules/userguide/media/guide/userguide/contrib-github-fork.png new file mode 100644 index 0000000000000000000000000000000000000000..a557b834137080af100beca13a787d652862bace GIT binary patch literal 8278 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 YPaAUG{B0TnhyDO*aRsp&5yPPW2hao!M*si- literal 0 HcmV?d00001 diff --git a/modules/userguide/media/guide/userguide/contrib-github-pull.png b/modules/userguide/media/guide/userguide/contrib-github-pull.png new file mode 100644 index 0000000000000000000000000000000000000000..e2bc6021aaa555dcbdb6f174220a7101dd366819 GIT binary patch literal 8839 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 array( + 'de-de' => 'Deutsch', + 'en-us' => 'English', + 'es-es' => 'Español', + 'zh-cn' => '简体中文', + 'ru-ru' => 'Русский', + 'fr-fr' => 'Français', + 'he-il' => 'עברית', + 'nl' => 'Nederlands', + ), +); diff --git a/modules/userguide/tests/KodocTest.php b/modules/userguide/tests/KodocTest.php new file mode 100644 index 0000000..8258f51 --- /dev/null +++ b/modules/userguide/tests/KodocTest.php @@ -0,0 +1,368 @@ +Description

    \n", array()), + ), + array( +<<<'COMMENT' +/** + * Description spanning + * multiple lines + */ +COMMENT +, + array("

    Description spanning\nmultiple lines

    \n", array()), + ), + array( +<<<'COMMENT' +/** + * Description including + * + * a code block + */ +COMMENT +, + array("

    Description including

    \n\n
    a code block\n
    \n", array()), + ), + array( +<<<'COMMENT' + /** + * Indented + */ +COMMENT +, + array("

    Indented

    \n", array()), + ), + array( +<<<'COMMENT' +/** + * @tag Content + */ +COMMENT +, + array('', array('tag' => array('Content'))), + ), + array( +<<<'COMMENT' +/** + * @tag Multiple + * @tag Tags + */ +COMMENT +, + array('', array('tag' => array('Multiple', 'Tags'))), + ), + array( +<<<'COMMENT' +/** + * Description with tag + * @tag Content + */ +COMMENT +, + array( + "

    Description with tag

    \n", + array('tag' => array('Content')), + ), + ), + array( +<<<'COMMENT' +/** + * @trailingspace + */ +COMMENT +, + array('', array('trailingspace' => array(''))), + ), + array( +<<<'COMMENT' +/** + * @tag Content that spans + * multiple lines + */ +COMMENT +, + array( + '', + array('tag' => array("Content that spans\nmultiple lines")), + ), + ), + array( +<<<'COMMENT' +/** + * @tag Content that spans + * multiple lines indented + */ +COMMENT +, + array( + '', + array('tag' => array("Content that spans\n multiple lines indented")), + ), + ), + ); + } + + /** + * @covers Kohana_Kodoc::parse + * + * @dataProvider provider_parse_basic + * + * @param string $comment Argument to the method + * @param array $expected Expected result + */ + public function test_parse_basic($comment, $expected) + { + $this->assertSame($expected, Kodoc::parse($comment)); + } + + public function provider_parse_tags() + { + $route_api = Route::get('docs/api'); + + return array( + array( +<<<'COMMENT' +/** + * @access public + */ +COMMENT +, + array('', array()), + ), + array( +<<<'COMMENT' +/** + * @copyright Some plain text + */ +COMMENT +, + array('', array('copyright' => array('Some plain text'))), + ), + array( +<<<'COMMENT' +/** + * @copyright (c) 2012 Kohana Team + */ +COMMENT +, + array('', array('copyright' => array('© 2012 Kohana Team'))), + ), + array( +<<<'COMMENT' +/** + * @license Kohana + */ +COMMENT +, + array('', array('license' => array('Kohana'))), + ), + array( +<<<'COMMENT' +/** + * @license http://kohanaframework.org/license + */ +COMMENT +, + array('', array('license' => array('http://kohanaframework.org/license'))), + ), + array( +<<<'COMMENT' +/** + * @link http://kohanaframework.org + */ +COMMENT +, + array('', array('link' => array('http://kohanaframework.org'))), + ), + array( +<<<'COMMENT' +/** + * @link http://kohanaframework.org Description + */ +COMMENT +, + array('', array('link' => array('Description'))), + ), + array( +<<<'COMMENT' +/** + * @see MyClass + */ +COMMENT +, + array( + '', + array( + 'see' => array( + 'MyClass', + ), + ), + ), + ), + array( +<<<'COMMENT' +/** + * @see MyClass::method() + */ +COMMENT +, + array( + '', + array( + 'see' => array( + 'MyClass::method()', + ), + ), + ), + ), + array( +<<<'COMMENT' +/** + * @throws Exception + */ +COMMENT +, + array( + '', + array( + 'throws' => array( + 'Exception', + ), + ), + ), + ), + array( +<<<'COMMENT' +/** + * @throws Exception During failure + */ +COMMENT +, + array( + '', + array( + 'throws' => array( + 'Exception During failure', + ), + ), + ), + ), + array( +<<<'COMMENT' +/** + * @uses MyClass + */ +COMMENT +, + array( + '', + array( + 'uses' => array( + 'MyClass', + ), + ), + ), + ), + array( +<<<'COMMENT' +/** + * @uses MyClass::method() + */ +COMMENT +, + array( + '', + array( + 'uses' => array( + 'MyClass::method()', + ), + ), + ), + ), + ); + } + + /** + * @covers Kohana_Kodoc::format_tag + * @covers Kohana_Kodoc::parse + * + * @dataProvider provider_parse_tags + * + * @param string $comment Argument to the method + * @param array $expected Expected result + */ + public function test_parse_tags($comment, $expected) + { + $this->assertSame($expected, Kodoc::parse($comment)); + } + + /** + * Provides test data for test_transparent_classes + * @return array + */ + public function provider_transparent_classes() + { + return array( + // Kohana_Core is a special case + array('Kohana','Kohana_Core',NULL), + array('Controller_Template','Kohana_Controller_Template',NULL), + array('Controller_Template','Kohana_Controller_Template', + array('Kohana_Controller_Template'=>'Kohana_Controller_Template', + 'Controller_Template'=>'Controller_Template') + ), + array(FALSE,'Kohana_Controller_Template', + array('Kohana_Controller_Template'=>'Kohana_Controller_Template')), + array(FALSE,'Controller_Template',NULL), + ); + } + + /** + * Tests Kodoc::is_transparent + * + * Checks that a selection of transparent and non-transparent classes give expected results + * + * @group kohana.userguide.3529-configurable-transparent-classes + * @dataProvider provider_transparent_classes + * @param mixed $expected + * @param string $class + * @param array $classes + */ + public function test_transparent_classes($expected, $class, $classes) + { + $result = Kodoc::is_transparent($class, $classes); + $this->assertSame($expected,$result); + } +} diff --git a/modules/userguide/tests/userguide/ControllerTest.php b/modules/userguide/tests/userguide/ControllerTest.php new file mode 100644 index 0000000..30ef475 --- /dev/null +++ b/modules/userguide/tests/userguide/ControllerTest.php @@ -0,0 +1,45 @@ +getMock('Controller_Userguide', array('__construct'), array(), '', FALSE); + $path = $controller->file($page); + + // Only verify trailing segments to avoid problems if file overwritten in CFS + $expected_len = strlen($expected_file); + $file = substr($path, -$expected_len, $expected_len); + + $this->assertEquals($expected_file, $file); + } +} diff --git a/modules/userguide/vendor/markdown/License.text b/modules/userguide/vendor/markdown/License.text new file mode 100644 index 0000000..52c868b --- /dev/null +++ b/modules/userguide/vendor/markdown/License.text @@ -0,0 +1,36 @@ +PHP Markdown & Extra +Copyright (c) 2004-2008 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2006 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/modules/userguide/vendor/markdown/markdown.php b/modules/userguide/vendor/markdown/markdown.php new file mode 100644 index 0000000..b649f6c --- /dev/null +++ b/modules/userguide/vendor/markdown/markdown.php @@ -0,0 +1,2909 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# + + +define( 'MARKDOWN_VERSION', "1.0.1m" ); # Sat 21 Jun 2008 +define( 'MARKDOWNEXTRA_VERSION', "1.2.3" ); # Wed 31 Dec 2008 + + +# +# Global default settings: +# + +# Change to ">" for HTML output +@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />"); + +# Define the width of a tab for code blocks. +@define( 'MARKDOWN_TAB_WIDTH', 4 ); + +# Optional title attribute for footnote links and backlinks. +@define( 'MARKDOWN_FN_LINK_TITLE', "" ); +@define( 'MARKDOWN_FN_BACKLINK_TITLE', "" ); + +# Optional class attribute for footnote links and backlinks. +@define( 'MARKDOWN_FN_LINK_CLASS', "" ); +@define( 'MARKDOWN_FN_BACKLINK_CLASS', "" ); + + +# +# WordPress settings: +# + +# Change to false to remove Markdown from posts and/or comments. +@define( 'MARKDOWN_WP_POSTS', true ); +@define( 'MARKDOWN_WP_COMMENTS', true ); + + + +### Standard Function Interface ### + +@define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' ); + +function Markdown($text) { +# +# Initialize the parser and return the result of its transform method. +# + # Setup static parser variable. + static $parser; + if (!isset($parser)) { + $parser_class = MARKDOWN_PARSER_CLASS; + $parser = new $parser_class; + } + + # Transform text using parser. + return $parser->transform($text); +} + + +### WordPress Plugin Interface ### + +/* +Plugin Name: Markdown Extra +Plugin URI: http://www.michelf.com/projects/php-markdown/ +Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More... +Version: 1.2.2 +Author: Michel Fortin +Author URI: http://www.michelf.com/ +*/ + +if (isset($wp_version)) { + # More details about how it works here: + # + + # Post content and excerpts + # - Remove WordPress paragraph generator. + # - Run Markdown on excerpt, then remove all tags. + # - Add paragraph tag around the excerpt, but remove it for the excerpt rss. + if (MARKDOWN_WP_POSTS) { + remove_filter('the_content', 'wpautop'); + remove_filter('the_content_rss', 'wpautop'); + remove_filter('the_excerpt', 'wpautop'); + add_filter('the_content', 'mdwp_MarkdownPost', 6); + add_filter('the_content_rss', 'mdwp_MarkdownPost', 6); + add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6); + add_filter('get_the_excerpt', 'trim', 7); + add_filter('the_excerpt', 'mdwp_add_p'); + add_filter('the_excerpt_rss', 'mdwp_strip_p'); + + remove_filter('content_save_pre', 'balanceTags', 50); + remove_filter('excerpt_save_pre', 'balanceTags', 50); + add_filter('the_content', 'balanceTags', 50); + add_filter('get_the_excerpt', 'balanceTags', 9); + } + + # Add a footnote id prefix to posts when inside a loop. + function mdwp_MarkdownPost($text) { + static $parser; + if (!$parser) { + $parser_class = MARKDOWN_PARSER_CLASS; + $parser = new $parser_class; + } + if (is_single() || is_page() || is_feed()) { + $parser->fn_id_prefix = ""; + } else { + $parser->fn_id_prefix = get_the_ID() . "."; + } + return $parser->transform($text); + } + + # Comments + # - Remove WordPress paragraph generator. + # - Remove WordPress auto-link generator. + # - Scramble important tags before passing them to the kses filter. + # - Run Markdown on excerpt then remove paragraph tags. + if (MARKDOWN_WP_COMMENTS) { + remove_filter('comment_text', 'wpautop', 30); + remove_filter('comment_text', 'make_clickable'); + add_filter('pre_comment_content', 'Markdown', 6); + add_filter('pre_comment_content', 'mdwp_hide_tags', 8); + add_filter('pre_comment_content', 'mdwp_show_tags', 12); + add_filter('get_comment_text', 'Markdown', 6); + add_filter('get_comment_excerpt', 'Markdown', 6); + add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); + + global $mdwp_hidden_tags, $mdwp_placeholders; + $mdwp_hidden_tags = explode(' ', + '

     
  • '); + $mdwp_placeholders = explode(' ', str_rot13( + 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. + 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); + } + + function mdwp_add_p($text) { + if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) { + $text = '

    '.$text.'

    '; + $text = preg_replace('{\n{2,}}', "

    \n\n

    ", $text); + } + return $text; + } + + function mdwp_strip_p($t) { return preg_replace('{}i', '', $t); } + + function mdwp_hide_tags($text) { + global $mdwp_hidden_tags, $mdwp_placeholders; + return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); + } + function mdwp_show_tags($text) { + global $mdwp_hidden_tags, $mdwp_placeholders; + return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); + } +} + + +### bBlog Plugin Info ### + +function identify_modifier_markdown() { + return array( + 'name' => 'markdown', + 'type' => 'modifier', + 'nicename' => 'PHP Markdown Extra', + 'description' => 'A text-to-HTML conversion tool for web writers', + 'authors' => 'Michel Fortin and John Gruber', + 'licence' => 'GPL', + 'version' => MARKDOWNEXTRA_VERSION, + 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...', + ); +} + + +### Smarty Modifier Interface ### + +function smarty_modifier_markdown($text) { + return Markdown($text); +} + + +### Textile Compatibility Mode ### + +# Rename this file to "classTextile.php" and it can replace Textile everywhere. + +if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { + # Try to include PHP SmartyPants. Should be in the same directory. + @include_once 'smartypants.php'; + # Fake Textile class. It calls Markdown instead. + class Textile { + function TextileThis($text, $lite='', $encode='') { + if ($lite == '' && $encode == '') $text = Markdown($text); + if (function_exists('SmartyPants')) $text = SmartyPants($text); + return $text; + } + # Fake restricted version: restrictions are not supported for now. + function TextileRestricted($text, $lite='', $noimage='') { + return $this->TextileThis($text, $lite); + } + # Workaround to ensure compatibility with TextPattern 4.0.3. + function blockLite($text) { return $text; } + } +} + + + +# +# Markdown Parser Class +# + +class Markdown_Parser { + + # Regex to match balanced [brackets]. + # Needed to insert a maximum bracked depth while converting to PHP. + var $nested_brackets_depth = 6; + var $nested_brackets_re; + + var $nested_url_parenthesis_depth = 4; + var $nested_url_parenthesis_re; + + # Table of hash values for escaped characters: + var $escape_chars = '\`*_{}[]()>#+-.!'; + var $escape_chars_re; + + # Change to ">" for HTML output. + var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; + var $tab_width = MARKDOWN_TAB_WIDTH; + + # Change to `true` to disallow markup or entities. + var $no_markup = false; + var $no_entities = false; + + # Predefined urls and titles for reference links and images. + var $predef_urls = array(); + var $predef_titles = array(); + + + function Markdown_Parser() { + # + # Constructor function. Initialize appropriate member variables. + # + $this->_initDetab(); + $this->prepareItalicsAndBold(); + + $this->nested_brackets_re = + str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). + str_repeat('\])*', $this->nested_brackets_depth); + + $this->nested_url_parenthesis_re = + str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). + str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); + + $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; + + # Sort document, block, and span gamut in ascendent priority order. + asort($this->document_gamut); + asort($this->block_gamut); + asort($this->span_gamut); + } + + + # Internal hashes used during transformation. + var $urls = array(); + var $titles = array(); + var $html_hashes = array(); + + # Status flag to avoid invalid nesting. + var $in_anchor = false; + + + function setup() { + # + # Called before the transformation process starts to setup parser + # states. + # + # Clear global hashes. + $this->urls = $this->predef_urls; + $this->titles = $this->predef_titles; + $this->html_hashes = array(); + + $in_anchor = false; + } + + function teardown() { + # + # Called after the transformation process to clear any variable + # which may be taking up memory unnecessarly. + # + $this->urls = array(); + $this->titles = array(); + $this->html_hashes = array(); + } + + + function transform($text) { + # + # Main function. Performs some preprocessing on the input text + # and pass it through the document gamut. + # + $this->setup(); + + # Remove UTF-8 BOM and marker character in input, if present. + $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = preg_replace('{\r\n?}', "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->detab($text); + + # Turn block-level HTML blocks into hash entries + $text = $this->hashHTMLBlocks($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ ]*\n+/ . + $text = preg_replace('/^[ ]+$/m', '', $text); + + # Run document gamut methods. + foreach ($this->document_gamut as $method => $priority) { + $text = $this->$method($text); + } + + $this->teardown(); + + return $text . "\n"; + } + + var $document_gamut = array( + # Strip link definitions, store in hashes. + "stripLinkDefinitions" => 20, + + "runBasicBlockGamut" => 30, + ); + + + function stripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 + [ ]* + \n? # maybe *one* newline + [ ]* + ? # url = $2 + [ ]* + \n? # maybe one newline + [ ]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.*?) # title = $3 + [")] + [ ]* + )? # title is optional + (?:\n+|\Z) + }xm', + array(&$this, '_stripLinkDefinitions_callback'), + $text); + return $text; + } + function _stripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $this->urls[$link_id] = $matches[2]; + $this->titles[$link_id] =& $matches[3]; + return ''; # String that will replace the block + } + + + function hashHTMLBlocks($text) { + if ($this->no_markup) return $text; + + $less_than_tab = $this->tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + # + # * List "a" is made of tags which can be both inline or block-level. + # These will be treated block-level when the start tag is alone on + # its line, otherwise they're not matched here and will be taken as + # inline later. + # * List "b" is made of tags which are always block-level; + # + $block_tags_a_re = 'ins|del'; + $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. + 'script|noscript|form|fieldset|iframe|math'; + + # Regular expression for the content of a block tag. + $nested_tags_level = 4; + $attr = ' + (?> # optional tag attributes + \s # starts with whitespace + (?> + [^>"/]+ # text outside quotes + | + /+(?!>) # slash not followed by ">" + | + "[^"]*" # text inside double quotes (tolerate ">") + | + \'[^\']*\' # text inside single quotes (tolerate ">") + )* + )? + '; + $content = + str_repeat(' + (?> + [^<]+ # content without tag + | + <\2 # nested opening tag + '.$attr.' # attributes + (?> + /> + | + >', $nested_tags_level). # end of opening tag + '.*?'. # last level nested tag content + str_repeat(' + # closing nested tag + ) + | + <(?!/\2\s*> # other tags with a different name + ) + )*', + $nested_tags_level); + $content2 = str_replace('\2', '\3', $content); + + # First, look for nested blocks, e.g.: + #

    + #
    + # tags for inner block must be indented. + #
    + #
    + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
    ` and stop at the first `
    `. + $text = preg_replace_callback('{(?> + (?> + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + + # Match from `\n` to `\n`, handling nested tags + # in between. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_b_re.')# start tag = $2 + '.$attr.'> # attributes followed by > and \n + '.$content.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special version for tags of group a. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_a_re.')# start tag = $3 + '.$attr.'>[ ]*\n # attributes followed by > + '.$content2.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special case just for
    . It was easier to make a special + # case than to make the other regex more complicated. + + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + '.$attr.' # attributes + /?> # the matching end tag + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # Special case for standalone HTML comments: + + [ ]{0,'.$less_than_tab.'} + (?s: + + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # PHP and ASP-style processor instructions ( + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + ) + )}Sxmi', + array(&$this, '_hashHTMLBlocks_callback'), + $text); + + return $text; + } + function _hashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = $this->hashBlock($text); + return "\n\n$key\n\n"; + } + + + function hashPart($text, $boundary = 'X') { + # + # Called whenever a tag must be hashed when a function insert an atomic + # element in the text stream. Passing $text to through this function gives + # a unique text-token which will be reverted back when calling unhash. + # + # The $boundary argument specify what character should be used to surround + # the token. By convension, "B" is used for block elements that needs not + # to be wrapped into paragraph tags at the end, ":" is used for elements + # that are word separators and "X" is used in the general case. + # + # Swap back any tag hash found in $text so we do not have to `unhash` + # multiple times at the end. + $text = $this->unhash($text); + + # Then hash the block. + static $i = 0; + $key = "$boundary\x1A" . ++$i . $boundary; + $this->html_hashes[$key] = $text; + return $key; # String that will replace the tag. + } + + + function hashBlock($text) { + # + # Shortcut function for hashPart with block-level boundaries. + # + return $this->hashPart($text, 'B'); + } + + + var $block_gamut = array( + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + "doHeaders" => 10, + "doHorizontalRules" => 20, + + "doLists" => 40, + "doCodeBlocks" => 50, + "doBlockQuotes" => 60, + ); + + function runBlockGamut($text) { + # + # Run block gamut tranformations. + # + # We need to escape raw HTML in Markdown source before doing anything + # else. This need to be done for each block, and not only at the + # begining in the Markdown function since hashed blocks can be part of + # list items and could have been indented. Indented blocks would have + # been seen as a code block in a previous pass of hashHTMLBlocks. + $text = $this->hashHTMLBlocks($text); + + return $this->runBasicBlockGamut($text); + } + + function runBasicBlockGamut($text) { + # + # Run block gamut tranformations, without hashing HTML blocks. This is + # useful when HTML blocks are known to be already hashed, like in the first + # whole-document pass. + # + foreach ($this->block_gamut as $method => $priority) { + $text = $this->$method($text); + } + + # Finally form paragraph and restore hashed blocks. + $text = $this->formParagraphs($text); + + return $text; + } + + + function doHorizontalRules($text) { + # Do Horizontal Rules: + return preg_replace( + '{ + ^[ ]{0,3} # Leading space + ([-*_]) # $1: First marker + (?> # Repeated marker group + [ ]{0,2} # Zero, one, or two spaces. + \1 # Marker character + ){2,} # Group repeated at least twice + [ ]* # Tailing spaces + $ # End of line. + }mx', + "\n".$this->hashBlock("empty_element_suffix")."\n", + $text); + } + + + var $span_gamut = array( + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + # Process character escapes, code spans, and inline HTML + # in one shot. + "parseSpan" => -30, + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + "doImages" => 10, + "doAnchors" => 20, + + # Make links out of things like `` + # Must come after doAnchors, because you can use < and > + # delimiters in inline links like [this](). + "doAutoLinks" => 30, + "encodeAmpsAndAngles" => 40, + + "doItalicsAndBold" => 50, + "doHardBreaks" => 60, + ); + + function runSpanGamut($text) { + # + # Run span gamut tranformations. + # + foreach ($this->span_gamut as $method => $priority) { + $text = $this->$method($text); + } + + return $text; + } + + + function doHardBreaks($text) { + # Do hard breaks: + return preg_replace_callback('/ {2,}\n/', + array(&$this, '_doHardBreaks_callback'), $text); + } + function _doHardBreaks_callback($matches) { + return $this->hashPart("empty_element_suffix\n"); + } + + + function doAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML tags. + # + if ($this->in_anchor) return $text; + $this->in_anchor = true; + + # + # First, handle reference-style links: [link text] [id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + \( # literal paren + [ ]* + (?: + <(\S*)> # href = $3 + | + ('.$this->nested_url_parenthesis_re.') # href = $4 + ) + [ ]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # Title = $7 + \6 # matching quote + [ ]* # ignore any spaces/tabs between closing quote and ) + )? # title is optional + \) + ) + }xs', + array(&$this, '_DoAnchors_inline_callback'), $text); + + # + # Last, handle reference-style shortcuts: [link text] + # These must come last in case you've also got [link test][1] + # or [link test](/foo) + # +// $text = preg_replace_callback('{ +// ( # wrap whole match in $1 +// \[ +// ([^\[\]]+) # link text = $2; can\'t contain [ or ] +// \] +// ) +// }xs', +// array(&$this, '_doAnchors_reference_callback'), $text); + + $this->in_anchor = false; + return $text; + } + function _doAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id =& $matches[3]; + + if ($link_id == "") { + # for shortcut links like [this][] or [this]. + $link_id = $link_text; + } + + # lower-case and turn embedded newlines into spaces + $link_id = strtolower($link_id); + $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); + + if (isset($this->urls[$link_id])) { + $url = $this->urls[$link_id]; + $url = $this->encodeAttribute($url); + + $result = "titles[$link_id] ) ) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + $result = $this->hashPart($result); + } + else { + $result = $whole_match; + } + return $result; + } + function _doAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $this->runSpanGamut($matches[2]); + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $url = $this->encodeAttribute($url); + + $result = "encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + + return $this->hashPart($result); + } + + + function doImages($text) { + # + # Turn Markdown image shortcuts into tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array(&$this, '_doImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + \s? # One optional whitespace character + \( # literal paren + [ ]* + (?: + <(\S*)> # src url = $3 + | + ('.$this->nested_url_parenthesis_re.') # src url = $4 + ) + [ ]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # title = $7 + \6 # matching quote + [ ]* + )? # title is optional + \) + ) + }xs', + array(&$this, '_doImages_inline_callback'), $text); + + return $text; + } + function _doImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = $this->encodeAttribute($alt_text); + if (isset($this->urls[$link_id])) { + $url = $this->encodeAttribute($this->urls[$link_id]); + $result = "\"$alt_text\"";titles[$link_id])) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + $result .= $this->empty_element_suffix; + $result = $this->hashPart($result); + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + function _doImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $alt_text = $this->encodeAttribute($alt_text); + $url = $this->encodeAttribute($url); + $result = "\"$alt_text\"";encodeAttribute($title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->empty_element_suffix; + + return $this->hashPart($result); + } + + + function doHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + function _doHeaders_callback_setext($matches) { + # Terrible hack to check we haven't found an empty list item. + if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) + return $matches[0]; + + $level = $matches[2]{0} == '=' ? 1 : 2; + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + function doLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $markers_relist = array($marker_ul_re, $marker_ol_re); + + foreach ($markers_relist as $marker_re) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list_re = ' + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ('.$marker_re.') # $3 = first list item marker + [ ]+ + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ ]* + '.$marker_re.'[ ]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n)\n|\A\n?) # Must eat the newline + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + } + + return $text; + } + function _doLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul_re/", $matches[3]) ? "ul" : "ol"; + + $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); + + $list .= "\n"; + $result = $this->processListItems($list, $marker_any_re); + + $result = $this->hashBlock("<$list_type>\n" . $result . ""); + return "\n". $result ."\n\n"; + } + + var $list_level = 0; + + function processListItems($list_str, $marker_any_re) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + # The $this->list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ ]*) # leading whitespace = $2 + ('.$marker_any_re.' # list marker and space = $3 + (?:[ ]+|(?=\n)) # space only required if item is not empty + ) + ((?s:.*?)) # list item text = $4 + (?:(\n+(?=\n))|\n) # tailing blank line = $5 + (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) + }xm', + array(&$this, '_processListItems_callback'), $list_str); + + $this->list_level--; + return $list_str; + } + function _processListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + $marker_space = $matches[3]; + $tailing_blank_line =& $matches[5]; + + if ($leading_line || $tailing_blank_line || + preg_match('/\n{2,}/', $item)) + { + # Replace marker with the appropriate whitespace indentation + $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; + $item = $this->runBlockGamut($this->outdent($item)."\n"); + } + else { + # Recursion for sub-lists: + $item = $this->doLists($this->outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->runSpanGamut($item); + } + + return "
  • " . $item . "
  • \n"; + } + + + function doCodeBlocks($text) { + # + # Process Markdown `
    ` blocks.
    +	#
    +		$text = preg_replace_callback('{
    +				(?:\n\n|\A\n?)
    +				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    +				  (?>
    +					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    +					.*\n+
    +				  )+
    +				)
    +				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    +			}xm',
    +			array(&$this, '_doCodeBlocks_callback'), $text);
    +
    +		return $text;
    +	}
    +	function _doCodeBlocks_callback($matches) {
    +		$codeblock = $matches[1];
    +
    +		$codeblock = $this->outdent($codeblock);
    +		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    +
    +		# trim leading newlines and trailing newlines
    +		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    +
    +		$codeblock = "
    $codeblock\n
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + + + function makeCodeSpan($code) { + # + # Create a code span markup for $code. Called from handleSpanToken. + # + $code = htmlspecialchars(trim($code), ENT_NOQUOTES); + return $this->hashPart("$code"); + } + + + var $em_relist = array( + '' => '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(?em_relist as $em => $em_re) { + foreach ($this->strong_relist as $strong => $strong_re) { + # Construct list of allowed token expressions. + $token_relist = array(); + if (isset($this->em_strong_relist["$em$strong"])) { + $token_relist[] = $this->em_strong_relist["$em$strong"]; + } + $token_relist[] = $em_re; + $token_relist[] = $strong_re; + + # Construct master expression from list. + $token_re = '{('. implode('|', $token_relist) .')}'; + $this->em_strong_prepared_relist["$em$strong"] = $token_re; + } + } + } + + function doItalicsAndBold($text) { + $token_stack = array(''); + $text_stack = array(''); + $em = ''; + $strong = ''; + $tree_char_em = false; + + while (1) { + # + # Get prepared regular expression for seraching emphasis tokens + # in current context. + # + $token_re = $this->em_strong_prepared_relist["$em$strong"]; + + # + # Each loop iteration seach for the next emphasis token. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + $text_stack[0] .= $parts[0]; + $token =& $parts[1]; + $text =& $parts[2]; + + if (empty($token)) { + # Reached end of text span: empty stack without emitting. + # any more emphasis. + while ($token_stack[0]) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + break; + } + + $token_len = strlen($token); + if ($tree_char_em) { + # Reached closing marker while inside a three-char emphasis. + if ($token_len == 3) { + # Three-char closing marker, close em and strong. + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + $strong = ''; + } else { + # Other closing marker: close one em or strong and + # change current token state to match the other + $token_stack[0] = str_repeat($token{0}, 3-$token_len); + $tag = $token_len == 2 ? "strong" : "em"; + $span = $text_stack[0]; + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] = $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + $tree_char_em = false; + } else if ($token_len == 3) { + if ($em) { + # Reached closing marker for both em and strong. + # Closing strong marker: + for ($i = 0; $i < 2; ++$i) { + $shifted_token = array_shift($token_stack); + $tag = strlen($shifted_token) == 2 ? "strong" : "em"; + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] .= $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + } else { + # Reached opening three-char emphasis marker. Push on token + # stack; will be handled by the special condition above. + $em = $token{0}; + $strong = "$em$em"; + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $tree_char_em = true; + } + } else if ($token_len == 2) { + if ($strong) { + # Unwind any dangling emphasis marker: + if (strlen($token_stack[0]) == 1) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + # Closing strong marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $strong = ''; + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $strong = $token; + } + } else { + # Here $token_len == 1 + if ($em) { + if (strlen($token_stack[0]) == 1) { + # Closing emphasis marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + } else { + $text_stack[0] .= $token; + } + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $em = $token; + } + } + } + return $text_stack[0]; + } + + + function doBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + (?> + ^[ ]*>[ ]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array(&$this, '_doBlockQuotes_callback'), $text); + + return $text; + } + function _doBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); + $bq = $this->runBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces cause problem with
     content, 
    +		# so we need to fix that:
    +		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', + array(&$this, '_DoBlockQuotes_callback2'), $bq); + + return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; + } + function _doBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + function formParagraphs($text) { + # + # Params: + # $text - string to process with html

    tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { + # Is a paragraph. + $value = $this->runSpanGamut($value); + $value = preg_replace('/^([ ]*)/', "

    ", $value); + $value .= "

    "; + $grafs[$key] = $this->unhash($value); + } + else { + # Is a block. + # Modify elements of @grafs in-place... + $graf = $value; + $block = $this->html_hashes[$graf]; + $graf = $block; +// if (preg_match('{ +// \A +// ( # $1 =
    tag +//
    ]* +// \b +// markdown\s*=\s* ([\'"]) # $2 = attr quote char +// 1 +// \2 +// [^>]* +// > +// ) +// ( # $3 = contents +// .* +// ) +// (
    ) # $4 = closing tag +// \z +// }xs', $block, $matches)) +// { +// list(, $div_open, , $div_content, $div_close) = $matches; +// +// # We can't call Markdown(), because that resets the hash; +// # that initialization code should be pulled into its own sub, though. +// $div_content = $this->hashHTMLBlocks($div_content); +// +// # Run document gamut methods on the content. +// foreach ($this->document_gamut as $method => $priority) { +// $div_content = $this->$method($div_content); +// } +// +// $div_open = preg_replace( +// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); +// +// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; +// } + $grafs[$key] = $graf; + } + } + + return implode("\n\n", $grafs); + } + + + function encodeAttribute($text) { + # + # Encode text for a double-quoted HTML attribute. This function + # is *not* suitable for attributes enclosed in single quotes. + # + $text = $this->encodeAmpsAndAngles($text); + $text = str_replace('"', '"', $text); + return $text; + } + + + function encodeAmpsAndAngles($text) { + # + # Smart processing for ampersands and angle brackets that need to + # be encoded. Valid character entities are left alone unless the + # no-entities mode is set. + # + if ($this->no_entities) { + $text = str_replace('&', '&', $text); + } else { + # Ampersand-encoding based entirely on Nat Irons's Amputator + # MT plugin: + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + } + # Encode remaining <'s + $text = str_replace('<', '<', $text); + + return $text; + } + + + function doAutoLinks($text) { + $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', + array(&$this, '_doAutoLinks_url_callback'), $text); + + # Email addresses: + $text = preg_replace_callback('{ + < + (?:mailto:)? + ( + [-.\w\x80-\xFF]+ + \@ + [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ + ) + > + }xi', + array(&$this, '_doAutoLinks_email_callback'), $text); + + return $text; + } + function _doAutoLinks_url_callback($matches) { + $url = $this->encodeAttribute($matches[1]); + $link = "$url"; + return $this->hashPart($link); + } + function _doAutoLinks_email_callback($matches) { + $address = $matches[1]; + $link = $this->encodeEmailAddress($address); + return $this->hashPart($link); + } + + + function encodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + #

    foo@exampl + # e.com

    + # + # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. + # With some optimizations by Milian Wolff. + # + $addr = "mailto:" . $addr; + $chars = preg_split('/(? $char) { + $ord = ord($char); + # Ignore non-ascii chars. + if ($ord < 128) { + $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') /* do nothing */; + else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; + else $chars[$key] = '&#'.$ord.';'; + } + } + + $addr = implode('', $chars); + $text = implode('', array_slice($chars, 7)); # text without `mailto:` + $addr = "$text"; + + return $addr; + } + + + function parseSpan($str) { + # + # Take the string $str and parse it into tokens, hashing embeded HTML, + # escaped characters and handling code spans. + # + $output = ''; + + $span_re = '{ + ( + \\\\'.$this->escape_chars_re.' + | + (?no_markup ? '' : ' + | + # comment + | + <\?.*?\?> | <%.*?%> # processing instruction + | + <[/!$]?[-a-zA-Z0-9:]+ # regular tags + (?> + \s + (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* + )? + > + ').' + ) + }xs'; + + while (1) { + # + # Each loop iteration seach for either the next tag, the next + # openning code span marker, or the next escaped character. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); + + # Create token from text preceding tag. + if ($parts[0] != "") { + $output .= $parts[0]; + } + + # Check if we reach the end. + if (isset($parts[1])) { + $output .= $this->handleSpanToken($parts[1], $parts[2]); + $str = $parts[2]; + } + else { + break; + } + } + + return $output; + } + + + function handleSpanToken($token, &$str) { + # + # Handle $token provided by parseSpan by determining its nature and + # returning the corresponding value that should replace it. + # + switch ($token{0}) { + case "\\": + return $this->hashPart("&#". ord($token{1}). ";"); + case "`": + # Search for end marker in remaining text. + if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', + $str, $matches)) + { + $str = $matches[2]; + $codespan = $this->makeCodeSpan($matches[1]); + return $this->hashPart($codespan); + } + return $token; // return as text since no ending marker found. + default: + return $this->hashPart($token); + } + } + + + function outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); + } + + + # String length function for detab. `_initDetab` will create a function to + # hanlde UTF-8 if the default function does not exist. + var $utf8_strlen = 'mb_strlen'; + + function detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $text = preg_replace_callback('/^.*\t.*$/m', + array(&$this, '_detab_callback'), $text); + + return $text; + } + function _detab_callback($matches) { + $line = $matches[0]; + $strlen = $this->utf8_strlen; # strlen function for UTF-8. + + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->tab_width - + $strlen($line, 'UTF-8') % $this->tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + return $line; + } + function _initDetab() { + # + # Check for the availability of the function in the `utf8_strlen` property + # (initially `mb_strlen`). If the function is not available, create a + # function that will loosely count the number of UTF-8 characters with a + # regular expression. + # + if (function_exists($this->utf8_strlen)) return; + $this->utf8_strlen = create_function('$text', 'return preg_match_all( + "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", + $text, $m);'); + } + + + function unhash($text) { + # + # Swap back in all the tags hashed by _HashHTMLBlocks. + # + return preg_replace_callback('/(.)\x1A[0-9]+\1/', + array(&$this, '_unhash_callback'), $text); + } + function _unhash_callback($matches) { + return $this->html_hashes[$matches[0]]; + } + +} + + +# +# Markdown Extra Parser Class +# + +class MarkdownExtra_Parser extends Markdown_Parser { + + # Prefix for footnote ids. + var $fn_id_prefix = ""; + + # Optional title attribute for footnote links and backlinks. + var $fn_link_title = MARKDOWN_FN_LINK_TITLE; + var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE; + + # Optional class attribute for footnote links and backlinks. + var $fn_link_class = MARKDOWN_FN_LINK_CLASS; + var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS; + + # Predefined abbreviations. + var $predef_abbr = array(); + + + function MarkdownExtra_Parser() { + # + # Constructor function. Initialize the parser object. + # + # Add extra escapable characters before parent constructor + # initialize the table. + $this->escape_chars .= ':|'; + + # Insert extra document, block, and span transformations. + # Parent constructor will do the sorting. + $this->document_gamut += array( + "doFencedCodeBlocks" => 5, + "stripFootnotes" => 15, + "stripAbbreviations" => 25, + "appendFootnotes" => 50, + ); + $this->block_gamut += array( + "doFencedCodeBlocks" => 5, + "doTables" => 15, + "doDefLists" => 45, + ); + $this->span_gamut += array( + "doFootnotes" => 5, + "doAbbreviations" => 70, + ); + + parent::Markdown_Parser(); + } + + + # Extra variables used during extra transformations. + var $footnotes = array(); + var $footnotes_ordered = array(); + var $abbr_desciptions = array(); + var $abbr_word_re = ''; + + # Give the current footnote number. + var $footnote_counter = 1; + + + function setup() { + # + # Setting up Extra-specific variables. + # + parent::setup(); + + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + $this->footnote_counter = 1; + + foreach ($this->predef_abbr as $abbr_word => $abbr_desc) { + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + } + } + + function teardown() { + # + # Clearing Extra-specific variables. + # + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + + parent::teardown(); + } + + + ### HTML Block Parser ### + + # Tags that are always treated as block tags: + var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend'; + + # Tags treated as block tags only if the opening tag is alone on it's line: + var $context_block_tags_re = 'script|noscript|math|ins|del'; + + # Tags where markdown="1" default to span mode: + var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; + + # Tags which must not have their contents modified, no matter where + # they appear: + var $clean_tags_re = 'script|math'; + + # Tags that do not need to be closed. + var $auto_close_tags_re = 'hr|img'; + + + function hashHTMLBlocks($text) { + # + # Hashify HTML Blocks and "clean tags". + # + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded. + # + # This works by calling _HashHTMLBlocks_InMarkdown, which then calls + # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1" + # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back + # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag. + # These two functions are calling each other. It's recursive! + # + # + # Call the HTML-in-Markdown hasher. + # + list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text); + + return $text; + } + function _hashHTMLBlocks_inMarkdown($text, $indent = 0, + $enclosing_tag_re = '', $span = false) + { + # + # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags. + # + # * $indent is the number of space to be ignored when checking for code + # blocks. This is important because if we don't take the indent into + # account, something like this (which looks right) won't work as expected: + # + #

    + #
    + # Hello World. <-- Is this a Markdown code block or text? + #
    <-- Is this a Markdown code block or a real tag? + #
    + # + # If you don't like this, just don't indent the tag on which + # you apply the markdown="1" attribute. + # + # * If $enclosing_tag_re is not empty, stops at the first unmatched closing + # tag with that name. Nested tags supported. + # + # * If $span is true, text inside must treated as span. So any double + # newline will be replaced by a single newline so that it does not create + # paragraphs. + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to check for the presense of newlines around a block tag. + $newline_before_re = '/(?:^\n?|\n\n)*$/'; + $newline_after_re = + '{ + ^ # Start of text following the tag. + (?>[ ]*)? # Optional comment. + [ ]*\n # Must be followed by newline. + }xs'; + + # Regex to match any tag. + $block_tag_re = + '{ + ( # $2: Capture hole tag. + # Tag name. + '.$this->block_tags_re.' | + '.$this->context_block_tags_re.' | + '.$this->clean_tags_re.' | + (?!\s)'.$enclosing_tag_re.' + ) + (?: + (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name. + (?> + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + | + # Code span marker + `+ + '. ( !$span ? ' # If not in span. + | + # Indented code block + (?> ^[ ]*\n? | \n[ ]*\n ) + [ ]{'.($indent+4).'}[^\n]* \n + (?> + (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n + )* + | + # Fenced code block marker + (?> ^ | \n ) + [ ]{'.($indent).'}~~~+[ ]*\n + ' : '' ). ' # End (if not is span). + ) + }xs'; + + + $depth = 0; # Current depth inside the tag tree. + $parsed = ""; # Parsed text that will be returned. + + # + # Loop through every tag until we find the closing tag of the parent + # or loop until reaching the end of text if no parent tag specified. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($block_tag_re, $text, 2, + PREG_SPLIT_DELIM_CAPTURE); + + # If in Markdown span mode, add a empty-string span-level hash + # after each newline to prevent triggering any block element. + if ($span) { + $void = $this->hashPart("", ':'); + $newline = "$void\n"; + $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; + } + + $parsed .= $parts[0]; # Text before current tag. + + # If end of $text has been reached. Stop loop. + if (count($parts) < 3) { + $text = ""; + break; + } + + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + $tag_re = preg_quote($tag); # For use in a regular expression. + + # + # Check for: Code span marker + # + if ($tag{0} == "`") { + # Find corresponding end marker. + $tag_re = preg_quote($tag); + if (preg_match('{^(?>.+?|\n(?!\n))*?(?.*\n)+?'.$tag_re.' *\n}', $text, + $matches)) + { + # End marker found: pass text unchanged until marker. + $parsed .= $tag . $matches[0]; + $text = substr($text, strlen($matches[0])); + } + else { + # No end marker: just skip it. + $parsed .= $tag; + } + } + } + # + # Check for: Opening Block level tag or + # Opening Context Block tag (like ins and del) + # used as a block tag (tag is alone on it's line). + # + else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) || + ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) && + preg_match($newline_before_re, $parsed) && + preg_match($newline_after_re, $text) ) + ) + { + # Need to parse tag and following text using the HTML parser. + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true); + + # Make sure it stays outside of any paragraph by adding newlines. + $parsed .= "\n\n$block_text\n\n"; + } + # + # Check for: Clean tag (like script, math) + # HTML Comments, processing instructions. + # + else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Need to parse tag and following text using the HTML parser. + # (don't check for markdown attribute) + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false); + + $parsed .= $block_text; + } + # + # Check for: Tag with same name as enclosing tag. + # + else if ($enclosing_tag_re !== '' && + # Same name as enclosing tag. + preg_match('{^= 0); + + return array($parsed, $text); + } + function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) { + # + # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags. + # + # * Calls $hash_method to convert any blocks. + # * Stops when the first opening tag closes. + # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed. + # (it is not inside clean tags) + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to match `markdown` attribute inside of a tag. + $markdown_attr_re = ' + { + \s* # Eat whitespace before the `markdown` attribute + markdown + \s*=\s* + (?> + (["\']) # $1: quote delimiter + (.*?) # $2: attribute value + \1 # matching delimiter + | + ([^\s>]*) # $3: unquoted attribute value + ) + () # $4: make $3 always defined (avoid warnings) + }xs'; + + # Regex to match any tag. + $tag_re = '{ + ( # $2: Capture hole tag. + + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + ) + }xs'; + + $original_text = $text; # Save original text in case of faliure. + + $depth = 0; # Current depth inside the tag tree. + $block_text = ""; # Temporary text holder for current text. + $parsed = ""; # Parsed text that will be returned. + + # + # Get the name of the starting tag. + # (This pattern makes $base_tag_name_re safe without quoting.) + # + if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) + $base_tag_name_re = $matches[1]; + + # + # Loop through every tag until we find the corresponding closing tag. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + + if (count($parts) < 3) { + # + # End of $text reached with unbalenced tag(s). + # In that case, we return original text unchanged and pass the + # first character as filtered to prevent an infinite loop in the + # parent function. + # + return array($original_text{0}, substr($original_text, 1)); + } + + $block_text .= $parts[0]; # Text before current tag. + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + + # + # Check for: Auto-close tag (like
    ) + # Comments and Processing Instructions. + # + if (preg_match('{^auto_close_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Just add the tag to the block as if it was text. + $block_text .= $tag; + } + else { + # + # Increase/decrease nested tag count. Only do so if + # the tag's name match base tag's. + # + if (preg_match('{^mode = $attr_m[2] . $attr_m[3]; + $span_mode = $this->mode == 'span' || $this->mode != 'block' && + preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag); + + # Calculate indent before tag. + if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) { + $strlen = $this->utf8_strlen; + $indent = $strlen($matches[1], 'UTF-8'); + } else { + $indent = 0; + } + + # End preceding block with this tag. + $block_text .= $tag; + $parsed .= $this->$hash_method($block_text); + + # Get enclosing tag name for the ParseMarkdown function. + # (This pattern makes $tag_name_re safe without quoting.) + preg_match('/^<([\w:$]*)\b/', $tag, $matches); + $tag_name_re = $matches[1]; + + # Parse the content using the HTML-in-Markdown parser. + list ($block_text, $text) + = $this->_hashHTMLBlocks_inMarkdown($text, $indent, + $tag_name_re, $span_mode); + + # Outdent markdown text. + if ($indent > 0) { + $block_text = preg_replace("/^[ ]{1,$indent}/m", "", + $block_text); + } + + # Append tag content to parsed text. + if (!$span_mode) $parsed .= "\n\n$block_text\n\n"; + else $parsed .= "$block_text"; + + # Start over a new block. + $block_text = ""; + } + else $block_text .= $tag; + } + + } while ($depth > 0); + + # + # Hash last block text that wasn't processed inside the loop. + # + $parsed .= $this->$hash_method($block_text); + + return array($parsed, $text); + } + + + function hashClean($text) { + # + # Called whenever a tag must be hashed when a function insert a "clean" tag + # in $text, it pass through this function and is automaticaly escaped, + # blocking invalid nested overlap. + # + return $this->hashPart($text, 'C'); + } + + + function doHeaders($text) { + # + # Redefined to add id attribute support. + # + # Setext-style headers: + # Header 1 {#header1} + # ======== + # + # Header 2 {#header2} + # -------- + # + $text = preg_replace_callback( + '{ + (^.+?) # $1: Header text + (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute + [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer + }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 {#header1} + # ## Header 2 {#header2} + # ## Header 2 with closing hashes ## {#header3} + # ... + # ###### Header 6 {#header2} + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute + [ ]* + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + function _doHeaders_attr($attr) { + if (empty($attr)) return ""; + return " id=\"$attr\""; + } + function _doHeaders_callback_setext($matches) { + if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) + return $matches[0]; + $level = $matches[3]{0} == '=' ? 1 : 2; + $attr = $this->_doHeaders_attr($id =& $matches[2]); + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $attr = $this->_doHeaders_attr($id =& $matches[3]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + function doTables($text) { + # + # Form HTML tables. + # + $less_than_tab = $this->tab_width - 1; + # + # Find tables with leading pipe. + # + # | Header 1 | Header 2 + # | -------- | -------- + # | Cell 1 | Cell 2 + # | Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] # Optional leading pipe (present) + (.+) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + [ ]* # Allowed whitespace. + [|] .* \n # Row content. + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_doTable_leadingPipe_callback'), $text); + + # + # Find tables without leading pipe. + # + # Header 1 | Header 2 + # -------- | -------- + # Cell 1 | Cell 2 + # Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + (\S.*[|].*) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + .* [|] .* \n # Row content + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_DoTable_callback'), $text); + + return $text; + } + function _doTable_leadingPipe_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove leading pipe for each row. + $content = preg_replace('/^ *[|]/m', '', $content); + + return $this->_doTable_callback(array($matches[0], $head, $underline, $content)); + } + function _doTable_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove any tailing pipes for each line. + $head = preg_replace('/[|] *$/m', '', $head); + $underline = preg_replace('/[|] *$/m', '', $underline); + $content = preg_replace('/[|] *$/m', '', $content); + + # Reading alignement from header underline. + $separators = preg_split('/ *[|] */', $underline); + foreach ($separators as $n => $s) { + if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"'; + else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"'; + else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"'; + else $attr[$n] = ''; + } + + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $head = $this->parseSpan($head); + $headers = preg_split('/ *[|] */', $head); + $col_count = count($headers); + + # Write column headers. + $text = "
    \n"; + $text .= "\n"; + $text .= "\n"; + foreach ($headers as $n => $header) + $text .= " ".$this->runSpanGamut(trim($header))."\n"; + $text .= "\n"; + $text .= "\n"; + + # Split content by row. + $rows = explode("\n", trim($content, "\n")); + + $text .= "\n"; + foreach ($rows as $row) { + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $row = $this->parseSpan($row); + + # Split row by cell. + $row_cells = preg_split('/ *[|] */', $row, $col_count); + $row_cells = array_pad($row_cells, $col_count, ''); + + $text .= "\n"; + foreach ($row_cells as $n => $cell) + $text .= " ".$this->runSpanGamut(trim($cell))."\n"; + $text .= "\n"; + } + $text .= "\n"; + $text .= "
    "; + + return $this->hashBlock($text) . "\n"; + } + + + function doDefLists($text) { + # + # Form HTML definition lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable pattern to match any entire dl list: + $whole_list_re = '(?> + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ((?>.*\S.*\n)+) # $3 = defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another term + [ ]{0,'.$less_than_tab.'} + (?: \S.*\n )+? # defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?! # Negative lookahead for another definition + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + ) + ) + )'; // mx + + $text = preg_replace_callback('{ + (?>\A\n?|(?<=\n\n)) + '.$whole_list_re.' + }mx', + array(&$this, '_doDefLists_callback'), $text); + + return $text; + } + function _doDefLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $list = $matches[1]; + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $result = trim($this->processDefListItems($list)); + $result = "
    \n" . $result . "\n
    "; + return $this->hashBlock($result) . "\n\n"; + } + + + function processDefListItems($list_str) { + # + # Process the contents of a single definition list, splitting it + # into individual term and definition list items. + # + $less_than_tab = $this->tab_width - 1; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + # Process definition terms. + $list_str = preg_replace_callback('{ + (?>\A\n?|\n\n+) # leading line + ( # definition terms = $1 + [ ]{0,'.$less_than_tab.'} # leading whitespace + (?![:][ ]|[ ]) # negative lookahead for a definition + # mark (colon) or more whitespace. + (?> \S.* \n)+? # actual term (not whitespace). + ) + (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed + # with a definition mark. + }xm', + array(&$this, '_processDefListItems_callback_dt'), $list_str); + + # Process actual definitions. + $list_str = preg_replace_callback('{ + \n(\n+)? # leading line = $1 + ( # marker space = $2 + [ ]{0,'.$less_than_tab.'} # whitespace before colon + [:][ ]+ # definition mark (colon) + ) + ((?s:.+?)) # definition text = $3 + (?= \n+ # stop at next definition mark, + (?: # next term or end of text + [ ]{0,'.$less_than_tab.'} [:][ ] | +