From 927c43cb309fe32c61f3c0e4ecb1b597c8de6ac1 Mon Sep 17 00:00:00 2001 From: Vaclav Sir Date: Sat, 13 Oct 2012 13:37:53 +0200 Subject: [PATCH 1/3] Aktualizace na PHPUnit 3.7.7 --- HttpPHPUnit/Main/Main.php | 35 +- libs/PHPUnit/File/Iterator.php | 13 +- libs/PHPUnit/File/Iterator/Autoload.php | 66 + .../Iterator/Autoload.php.in} | 38 +- libs/PHPUnit/File/Iterator/Facade.php | 161 ++ libs/PHPUnit/File/Iterator/Factory.php | 53 +- libs/PHPUnit/PHP/CodeCoverage.php | 771 ++++-- libs/PHPUnit/PHP/CodeCoverage/Autoload.php | 90 + libs/PHPUnit/PHP/CodeCoverage/Autoload.php.in | 70 + libs/PHPUnit/PHP/CodeCoverage/Driver.php | 11 +- .../PHP/CodeCoverage/Driver/Xdebug.php | 40 +- libs/PHPUnit/PHP/CodeCoverage/Exception.php | 59 + libs/PHPUnit/PHP/CodeCoverage/Filter.php | 175 +- .../PHP/CodeCoverage/Report/Clover.php | 539 ++-- .../PHP/CodeCoverage/Report/Factory.php | 280 ++ libs/PHPUnit/PHP/CodeCoverage/Report/HTML.php | 404 +-- .../PHP/CodeCoverage/Report/HTML/Node.php | 512 ---- .../CodeCoverage/Report/HTML/Node/File.php | 916 ------ .../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 + .../Report/HTML/Template/RGraph.bar.js | 1653 ----------- .../HTML/Template/RGraph.common.core.js | 2454 ----------------- .../HTML/Template/RGraph.common.tooltips.js | 502 ---- .../Report/HTML/Template/RGraph.scatter.js | 1168 -------- .../Report/HTML/Template/butter.png | Bin 150 -> 0 bytes .../Report/HTML/Template/chameleon.png | Bin 150 -> 0 bytes .../Report/HTML/Template/close12_1.gif | Bin 85 -> 0 bytes .../Report/HTML/Template/container-min.js | 19 - .../Report/HTML/Template/container.css | 324 --- .../Report/HTML/Template/dashboard.html.dist | 86 - .../Report/HTML/Template/directory.html.dist | 71 - .../Report/HTML/Template/directory.png | Bin 581 -> 0 bytes .../HTML/Template/directory_item.html.dist | 31 - .../HTML/Template/excanvas.compressed.js | Bin 8660 -> 0 bytes .../Report/HTML/Template/file.html.dist | 120 - .../Report/HTML/Template/file.png | Bin 333 -> 0 bytes .../Report/HTML/Template/file_item.html.dist | 32 - .../HTML/Template/file_no_yui.html.dist | 79 - .../Report/HTML/Template/glass.png | Bin 167 -> 0 bytes .../HTML/Template/method_item.html.dist | 23 - .../Report/HTML/Template/scarlet_red.png | Bin 150 -> 0 bytes .../Report/HTML/Template/snow.png | Bin 141 -> 0 bytes .../Report/HTML/Template/style.css | 459 --- .../Report/HTML/Template/yahoo-dom-event.js | 14 - .../Report/HTML/Template/yui_item.js | 5 - libs/PHPUnit/PHP/CodeCoverage/Report/Node.php | 380 +++ .../Report/{HTML => }/Node/Directory.php | 312 ++- .../PHP/CodeCoverage/Report/Node/File.php | 721 +++++ .../Report/{HTML => }/Node/Iterator.php | 31 +- libs/PHPUnit/PHP/CodeCoverage/Report/PHP.php | 74 + libs/PHPUnit/PHP/CodeCoverage/Report/Text.php | 278 ++ .../PHP/CodeCoverage/TextUI/Command.php | 268 -- libs/PHPUnit/PHP/CodeCoverage/Util.php | 630 +---- .../Util/InvalidArgumentHelper.php | 80 + libs/PHPUnit/PHP/CodeCoverage/Version.php | 92 + libs/PHPUnit/PHP/Timer.php | 34 +- libs/PHPUnit/PHP/Timer/Autoload.php | 66 + libs/PHPUnit/PHP/Timer/Autoload.php.in | 66 + libs/PHPUnit/PHP/Token.php | 290 +- libs/PHPUnit/PHP/Token/Stream.php | 266 +- libs/PHPUnit/PHP/Token/Stream/Autoload.php | 226 ++ .../{Exception.php => Stream/Autoload.php.in} | 39 +- .../PHP/Token/Stream/CachingFactory.php | 26 +- .../PHP/Token/Stream/TextUI/Command.php | 181 -- libs/PHPUnit/PHPUnit/Autoload.php | 193 +- libs/PHPUnit/PHPUnit/Autoload.php.in | 91 + .../PHPUnit/Extensions/GroupTestSuite.php | 11 +- .../PHPUnit/Extensions/OutputTestCase.php | 202 -- .../PHPUnit/Extensions/PhptTestCase.php | 38 +- .../Extensions/PhptTestCase/Logger.php | 11 +- .../PHPUnit/Extensions/PhptTestSuite.php | 24 +- .../PHPUnit/Extensions/RepeatedTest.php | 17 +- .../Extensions/Story/ResultPrinter/HTML.php | 212 -- .../ResultPrinter/Template/scenario.html.dist | 13 - .../Template/scenario_header.html.dist | 6 - .../Template/scenarios.html.dist | 60 - .../ResultPrinter/Template/step.html.dist | 6 - .../Extensions/Story/ResultPrinter/Text.php | 150 - .../PHPUnit/Extensions/Story/Scenario.php | 189 -- .../PHPUnit/Extensions/Story/TestCase.php | 210 -- .../PHPUnit/Extensions/TestDecorator.php | 12 +- .../PHPUnit/Extensions/TicketListener.php | 11 +- .../Extensions/TicketListener/GitHub.php | 204 -- .../Extensions/TicketListener/GoogleCode.php | 275 -- .../Extensions/TicketListener/Trac.php | 189 -- libs/PHPUnit/PHPUnit/Framework/Assert.php | 616 +++-- .../PHPUnit/Framework/Assert/Functions.php | 295 +- .../PHPUnit/Framework/Assert/Functions.php.in | 6 +- .../Framework/AssertionFailedError.php | 13 +- libs/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 | 134 +- .../Framework/ComparisonFailure/Array.php | 140 - .../Framework/ComparisonFailure/Object.php | 170 -- libs/PHPUnit/PHPUnit/Framework/Constraint.php | 172 +- .../PHPUnit/Framework/Constraint/And.php | 72 +- .../Framework/Constraint/ArrayHasKey.php | 35 +- .../Framework/Constraint/Attribute.php | 90 +- .../Constraint/Callback.php} | 100 +- .../Constraint/ClassHasAttribute.php | 32 +- .../Constraint/ClassHasStaticAttribute.php | 15 +- .../Framework/Constraint/Composite.php | 114 + .../PHPUnit/Framework/Constraint/Count.php | 128 + .../Framework/Constraint/Exception.php | 129 + .../Framework/Constraint/ExceptionCode.php | 109 + .../Constraint/ExceptionMessage.php} | 85 +- .../Framework/Constraint/FileExists.php | 48 +- .../Framework/Constraint/GreaterThan.php | 17 +- .../Framework/Constraint/IsAnything.php | 45 +- .../PHPUnit/Framework/Constraint/IsEmpty.php | 40 +- .../PHPUnit/Framework/Constraint/IsEqual.php | 265 +- .../PHPUnit/Framework/Constraint/IsFalse.php | 15 +- .../Framework/Constraint/IsIdentical.php | 108 +- .../Framework/Constraint/IsInstanceOf.php | 45 +- .../PHPUnit/Framework/Constraint/IsNull.php | 15 +- .../PHPUnit/Framework/Constraint/IsTrue.php | 15 +- .../PHPUnit/Framework/Constraint/IsType.php | 27 +- .../Framework/Constraint/JsonMatches.php | 126 + .../JsonMatches/ErrorMessageProvider.php | 106 + .../PHPUnit/Framework/Constraint/LessThan.php | 17 +- .../PHPUnit/Framework/Constraint/Not.php | 112 +- .../Constraint/ObjectHasAttribute.php | 23 +- .../PHPUnit/Framework/Constraint/Or.php | 54 +- .../Framework/Constraint/PCREMatch.php | 15 +- .../Constraint/SameSize.php} | 40 +- .../Framework/Constraint/StringContains.php | 15 +- .../Framework/Constraint/StringEndsWith.php | 15 +- .../Framework/Constraint/StringMatches.php | 88 +- .../Framework/Constraint/StringStartsWith.php | 15 +- .../Constraint/TraversableContains.php | 53 +- .../Constraint/TraversableContainsOnly.php | 48 +- .../PHPUnit/Framework/Constraint/Xor.php | 62 +- libs/PHPUnit/PHPUnit/Framework/Error.php | 26 +- .../Error/Deprecated.php} | 36 +- .../PHPUnit/Framework/Error/Notice.php | 11 +- .../PHPUnit/Framework/Error/Warning.php | 11 +- libs/PHPUnit/PHPUnit/Framework/Exception.php | 11 +- .../Framework/ExpectationFailedException.php | 56 +- .../PHPUnit/Framework/IncompleteTest.php | 11 +- .../PHPUnit/Framework/IncompleteTestError.php | 11 +- .../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 +++ .../Matcher/AnyInvokedCount.php} | 49 +- .../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 + .../Scalar.php => MockObject/Stub/Return.php} | 56 +- .../Stub/ReturnArgument.php} | 64 +- .../MockObject/Stub/ReturnCallback.php | 94 + .../Framework/MockObject/Stub/ReturnSelf.php | 76 + .../MockObject/Stub/ReturnValueMap.php | 87 + .../MockObject/Verifiable.php} | 46 +- .../OutputError.php} | 29 +- .../Framework/Process/TestCaseMethod.tpl.dist | 9 +- .../PHPUnit/Framework/SelfDescribing.php | 11 +- .../PHPUnit/PHPUnit/Framework/SkippedTest.php | 11 +- .../PHPUnit/Framework/SkippedTestError.php | 11 +- .../Framework/SkippedTestSuiteError.php | 11 +- .../PHPUnit/Framework/SyntheticError.php | 11 +- libs/PHPUnit/PHPUnit/Framework/Test.php | 11 +- libs/PHPUnit/PHPUnit/Framework/TestCase.php | 613 +++- .../PHPUnit/PHPUnit/Framework/TestFailure.php | 71 +- .../PHPUnit/Framework/TestListener.php | 11 +- libs/PHPUnit/PHPUnit/Framework/TestResult.php | 257 +- libs/PHPUnit/PHPUnit/Framework/TestSuite.php | 118 +- .../Framework/TestSuite/DataProvider.php | 11 +- libs/PHPUnit/PHPUnit/Framework/Warning.php | 13 +- .../PHPUnit/PHPUnit/Runner/BaseTestRunner.php | 31 +- .../Runner/IncludePathTestCollector.php | 144 - .../Runner/StandardTestSuiteLoader.php | 24 +- .../PHPUnit/Runner/TestSuiteLoader.php | 11 +- libs/PHPUnit/PHPUnit/Runner/Version.php | 39 +- libs/PHPUnit/PHPUnit/TextUI/Command.php | 469 ++-- libs/PHPUnit/PHPUnit/TextUI/ResultPrinter.php | 86 +- libs/PHPUnit/PHPUnit/TextUI/TestRunner.php | 426 +-- libs/PHPUnit/PHPUnit/Util/Class.php | 104 +- libs/PHPUnit/PHPUnit/Util/Configuration.php | 294 +- .../PHPUnit/Util/DeprecatedFeature.php | 5 +- .../PHPUnit/Util/DeprecatedFeature/Logger.php | 5 +- libs/PHPUnit/PHPUnit/Util/Diff.php | 165 +- libs/PHPUnit/PHPUnit/Util/ErrorHandler.php | 22 +- libs/PHPUnit/PHPUnit/Util/File.php | 310 --- libs/PHPUnit/PHPUnit/Util/Fileloader.php | 57 +- libs/PHPUnit/PHPUnit/Util/Filesystem.php | 83 +- libs/PHPUnit/PHPUnit/Util/Filter.php | 56 +- libs/PHPUnit/PHPUnit/Util/Getopt.php | 11 +- libs/PHPUnit/PHPUnit/Util/GlobalState.php | 101 +- .../PHPUnit/Util/InvalidArgumentHelper.php | 16 +- libs/PHPUnit/PHPUnit/Util/Log/DBUS.php | 236 -- libs/PHPUnit/PHPUnit/Util/Log/JSON.php | 62 +- libs/PHPUnit/PHPUnit/Util/Log/JUnit.php | 23 +- libs/PHPUnit/PHPUnit/Util/Log/TAP.php | 19 +- libs/PHPUnit/PHPUnit/Util/Log/XHProf.php | 252 -- libs/PHPUnit/PHPUnit/Util/PHP.php | 153 +- libs/PHPUnit/PHPUnit/Util/PHP/Default.php | 11 +- libs/PHPUnit/PHPUnit/Util/PHP/Windows.php | 13 +- libs/PHPUnit/PHPUnit/Util/Printer.php | 23 +- libs/PHPUnit/PHPUnit/Util/Skeleton.php | 146 - libs/PHPUnit/PHPUnit/Util/Skeleton/Class.php | 327 --- .../Util/Skeleton/Template/Class.tpl.dist | 7 - .../Template/IncompleteTestMethod.tpl.dist | 11 - .../Util/Skeleton/Template/Method.tpl.dist | 9 - .../Util/Skeleton/Template/TestClass.tpl.dist | 31 - .../Skeleton/Template/TestMethod.tpl.dist | 11 - .../Skeleton/Template/TestMethodBool.tpl.dist | 10 - .../Template/TestMethodBoolStatic.tpl.dist | 10 - .../Template/TestMethodException.tpl.dist | 9 - .../TestMethodExceptionStatic.tpl.dist | 9 - .../Template/TestMethodStatic.tpl.dist | 11 - libs/PHPUnit/PHPUnit/Util/Skeleton/Test.php | 379 --- libs/PHPUnit/PHPUnit/Util/String.php | 118 + libs/PHPUnit/PHPUnit/Util/Test.php | 154 +- .../PHPUnit/Util/TestDox/NamePrettifier.php | 11 +- .../PHPUnit/Util/TestDox/ResultPrinter.php | 11 +- .../Util/TestDox/ResultPrinter/HTML.php | 11 +- .../Util/TestDox/ResultPrinter/Text.php | 11 +- .../PHPUnit/Util/TestSuiteIterator.php | 11 +- libs/PHPUnit/PHPUnit/Util/Type.php | 263 +- libs/PHPUnit/PHPUnit/Util/XML.php | 113 +- libs/PHPUnit/Text/Template.php | 12 +- libs/PHPUnit/Text/Template/Autoload.php | 34 +- libs/PHPUnit/Text/Template/Autoload.php.in | 65 + 284 files changed, 18021 insertions(+), 17924 deletions(-) create mode 100644 libs/PHPUnit/File/Iterator/Autoload.php rename libs/PHPUnit/{PHPUnit/Framework.php => File/Iterator/Autoload.php.in} (67%) create mode 100644 libs/PHPUnit/File/Iterator/Facade.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Autoload.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Autoload.php.in create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Exception.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/Factory.php delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node.php delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/File.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/File.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/jquery.min.js create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.bar.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.common.core.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.common.tooltips.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.scatter.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/butter.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/chameleon.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/close12_1.gif delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/container-min.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/container.css delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/dashboard.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory_item.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/excanvas.compressed.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_item.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_no_yui.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/glass.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/method_item.html.dist delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/scarlet_red.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/snow.png delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/style.css delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/yahoo-dom-event.js delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/yui_item.js create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/Node.php rename libs/PHPUnit/PHP/CodeCoverage/Report/{HTML => }/Node/Directory.php (57%) create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/Node/File.php rename libs/PHPUnit/PHP/CodeCoverage/Report/{HTML => }/Node/Iterator.php (77%) create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/PHP.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Report/Text.php delete mode 100644 libs/PHPUnit/PHP/CodeCoverage/TextUI/Command.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Util/InvalidArgumentHelper.php create mode 100644 libs/PHPUnit/PHP/CodeCoverage/Version.php create mode 100644 libs/PHPUnit/PHP/Timer/Autoload.php create mode 100644 libs/PHPUnit/PHP/Timer/Autoload.php.in create mode 100644 libs/PHPUnit/PHP/Token/Stream/Autoload.php rename libs/PHPUnit/PHP/Token/{Exception.php => Stream/Autoload.php.in} (78%) delete mode 100644 libs/PHPUnit/PHP/Token/Stream/TextUI/Command.php create mode 100644 libs/PHPUnit/PHPUnit/Autoload.php.in delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/OutputTestCase.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/HTML.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario.html.dist delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario_header.html.dist delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenarios.html.dist delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/step.html.dist delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Text.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/Scenario.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/Story/TestCase.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/TicketListener/GitHub.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/TicketListener/GoogleCode.php delete mode 100644 libs/PHPUnit/PHPUnit/Extensions/TicketListener/Trac.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Array.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/DOMDocument.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Double.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Exception.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/MockObject.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Numeric.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Object.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Resource.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Scalar.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/SplObjectStorage.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Comparator/Type.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/ComparatorFactory.php delete mode 100644 libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Array.php delete mode 100644 libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Object.php rename libs/PHPUnit/PHPUnit/{Extensions/Story/Step.php => Framework/Constraint/Callback.php} (55%) create mode 100644 libs/PHPUnit/PHPUnit/Framework/Constraint/Composite.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Constraint/Count.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Constraint/Exception.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Constraint/ExceptionCode.php rename libs/PHPUnit/PHPUnit/{Extensions/Story/ResultPrinter.php => Framework/Constraint/ExceptionMessage.php} (53%) create mode 100644 libs/PHPUnit/PHPUnit/Framework/Constraint/JsonMatches.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php rename libs/PHPUnit/PHPUnit/{Extensions/Story/When.php => Framework/Constraint/SameSize.php} (66%) rename libs/PHPUnit/PHPUnit/{Extensions/Story/Then.php => Framework/Error/Deprecated.php} (71%) create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Autoload.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Autoload.php.in create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Identity.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Match.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Namespace.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Stub.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation/Object.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation/Static.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/InvocationMocker.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Invokable.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher.php rename libs/PHPUnit/PHPUnit/Framework/{ComparisonFailure/Type.php => MockObject/Matcher/AnyInvokedCount.php} (57%) create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/Invocation.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/MethodName.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/Parameters.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/MockBuilder.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/MockObject.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/Exception.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php rename libs/PHPUnit/PHPUnit/Framework/{ComparisonFailure/Scalar.php => MockObject/Stub/Return.php} (55%) rename libs/PHPUnit/PHPUnit/Framework/{ComparisonFailure/String.php => MockObject/Stub/ReturnArgument.php} (52%) create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php create mode 100644 libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php rename libs/PHPUnit/PHPUnit/{Extensions/Story/Given.php => Framework/MockObject/Verifiable.php} (57%) rename libs/PHPUnit/PHPUnit/{Runner/TestCollector.php => Framework/OutputError.php} (72%) delete mode 100644 libs/PHPUnit/PHPUnit/Runner/IncludePathTestCollector.php delete mode 100644 libs/PHPUnit/PHPUnit/Util/File.php delete mode 100644 libs/PHPUnit/PHPUnit/Util/Log/DBUS.php delete mode 100644 libs/PHPUnit/PHPUnit/Util/Log/XHProf.php delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton.php delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Class.php delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Class.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/IncompleteTestMethod.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Method.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestClass.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethod.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBool.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBoolStatic.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodException.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodExceptionStatic.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodStatic.tpl.dist delete mode 100644 libs/PHPUnit/PHPUnit/Util/Skeleton/Test.php create mode 100644 libs/PHPUnit/PHPUnit/Util/String.php create mode 100644 libs/PHPUnit/Text/Template/Autoload.php.in diff --git a/HttpPHPUnit/Main/Main.php b/HttpPHPUnit/Main/Main.php index ceb7f96..5680d05 100644 --- a/HttpPHPUnit/Main/Main.php +++ b/HttpPHPUnit/Main/Main.php @@ -47,14 +47,18 @@ class Main extends Object /** * @param string path to PHPUnit + * @param bool $loadPHPUnit * @throws DirectoryNotFoundException */ - public function __construct($phpUnitDir = NULL) + public function __construct($phpUnitDir = NULL, $loadPHPUnit = true) { - if (!$phpUnitDir) $phpUnitDir = __DIR__ . '/../../PHPUnit'; - if (!is_dir($phpUnitDir)) throw new DirectoryNotFoundException($phpUnitDir); - set_include_path($phpUnitDir); - require_once 'PHPUnit/Autoload.php'; + if ($loadPHPUnit) + { + if (!$phpUnitDir) $phpUnitDir = __DIR__ . '/../../PHPUnit'; + if (!is_dir($phpUnitDir)) throw new DirectoryNotFoundException($phpUnitDir); + set_include_path($phpUnitDir); + require_once 'PHPUnit/Autoload.php'; + } require_once __DIR__ . '/Command.php'; require_once __DIR__ . '/TemplateFactory.php'; require_once __DIR__ . '/../ResultPrinter/ResultPrinter.php'; @@ -131,20 +135,21 @@ public function run($dir, $arg = '--no-globals-backup --strict') * @param string app dir * @param string report dir * @throws DirectoryNotFoundException - * @return PHP_CodeCoverage + * @return PHP_CodeCoverage|NULL */ public function coverage($appDir, $coverageDir) { - require_once 'PHP/CodeCoverage.php'; - $coverage = PHP_CodeCoverage::getInstance(); - if (!$this->run OR $this->testDir OR !extension_loaded('xdebug')) + if (!extension_loaded('xdebug')) + { + $this->onAfter['coverage'] = function () { + echo 'Coverage: The Xdebug extension is not loaded.'; + }; + return; + } + $coverage = new PHP_CodeCoverage; + $coverage->setProcessUncoveredFilesFromWhitelist(true); + if (!$this->run OR $this->testDir) { - if (!extension_loaded('xdebug')) - { - $this->onAfter['coverage'] = function () { - echo 'Coverage: The Xdebug extension is not loaded.'; - }; - } return $coverage; } @mkdir ($coverageDir); diff --git a/libs/PHPUnit/File/Iterator.php b/libs/PHPUnit/File/Iterator.php index aa0022b..73f19b9 100644 --- a/libs/PHPUnit/File/Iterator.php +++ b/libs/PHPUnit/File/Iterator.php @@ -2,7 +2,7 @@ /** * php-file-iterator * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,8 +36,8 @@ * * @package File * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -46,9 +46,9 @@ * suffix(es). Hidden files and files from hidden directories are also filtered. * * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.2.6 + * @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 */ @@ -194,4 +194,3 @@ protected function acceptSubString($filename, array $subStrings, $type) return $matched; } } -?> diff --git a/libs/PHPUnit/File/Iterator/Autoload.php b/libs/PHPUnit/File/Iterator/Autoload.php new file mode 100644 index 0000000..fcb2105 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework.php b/libs/PHPUnit/File/Iterator/Autoload.php.in similarity index 67% rename from libs/PHPUnit/PHPUnit/Framework.php rename to libs/PHPUnit/File/Iterator/Autoload.php.in index 55d0b5c..20d58b9 100644 --- a/libs/PHPUnit/PHPUnit/Framework.php +++ b/libs/PHPUnit/File/Iterator/Autoload.php.in @@ -1,8 +1,8 @@ . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,17 +34,31 @@ * 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-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.0.0 + * @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 */ -require_once 'PHP/CodeCoverage/Filter.php'; -PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist(__FILE__, 'PHPUNIT'); +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; -trigger_error( - 'Please no longer include "PHPUnit/Framework.php".', E_USER_NOTICE + if ($classes === NULL) { + $classes = array( + ___CLASSLIST___ + ); + + $path = dirname(dirname(__FILE__)); + } + + $cn = strtolower($class); + + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } ); diff --git a/libs/PHPUnit/File/Iterator/Facade.php b/libs/PHPUnit/File/Iterator/Facade.php new file mode 100644 index 0000000..d30283e --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/File/Iterator/Factory.php b/libs/PHPUnit/File/Iterator/Factory.php index b10729b..47a50ae 100644 --- a/libs/PHPUnit/File/Iterator/Factory.php +++ b/libs/PHPUnit/File/Iterator/Factory.php @@ -2,7 +2,7 @@ /** * php-file-iterator * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,22 +36,20 @@ * * @package File * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'File/Iterator.php'; - /** * 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-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.2.6 + * @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 */ @@ -64,7 +62,7 @@ class File_Iterator_Factory * @param array $exclude * @return AppendIterator */ - public static function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array()) + public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array()) { if (is_string($paths)) { $paths = array($paths); @@ -119,41 +117,4 @@ public static function getFileIterator($paths, $suffixes = '', $prefixes = '', a return $iterator; } - - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - * @param array $exclude - * @return array - */ - public static function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = array()) - { - if (is_string($paths)) { - $paths = array($paths); - } - - $result = array(); - - $iterator = self::getFileIterator( - $paths, $suffixes, $prefixes, $exclude - ); - - foreach ($iterator as $file) { - $file = $file->getRealPath(); - - if ($file) { - $result[] = $file; - } - } - - foreach ($paths as $path) { - if (is_file($path)) { - $result[] = realpath($path); - } - } - - return array_unique($result); - } } -?> diff --git a/libs/PHPUnit/PHP/CodeCoverage.php b/libs/PHPUnit/PHP/CodeCoverage.php index c3005b9..37de2c1 100644 --- a/libs/PHPUnit/PHP/CodeCoverage.php +++ b/libs/PHPUnit/PHP/CodeCoverage.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,15 +37,25 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/CodeCoverage/Driver/Xdebug.php'; -require_once 'PHP/CodeCoverage/Filter.php'; -require_once 'PHP/CodeCoverage/Util.php'; +// @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. @@ -53,9 +63,8 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 */ @@ -74,43 +83,39 @@ class PHP_CodeCoverage /** * @var boolean */ - protected $forceCoversAnnotation = FALSE; + protected $cacheTokens = FALSE; /** * @var boolean */ - protected $mapTestClassNameToCoveredClassName = FALSE; + protected $forceCoversAnnotation = FALSE; /** * @var boolean */ - protected $processUncoveredFilesFromWhitelist = TRUE; + protected $mapTestClassNameToCoveredClassName = FALSE; /** - * @var mixed + * @var boolean */ - protected $currentId; + protected $addUncoveredFilesFromWhitelist = TRUE; /** - * List of covered files. - * - * @var array + * @var boolean */ - protected $coveredFiles = array(); + protected $processUncoveredFilesFromWhitelist = FALSE; /** - * Raw code coverage data. - * - * @var array + * @var mixed */ - protected $data = array(); + protected $currentId; /** - * Summarized code coverage data. + * Code coverage data. * * @var array */ - protected $summary = array(); + protected $data = array(); /** * Test data. @@ -120,82 +125,89 @@ class PHP_CodeCoverage protected $tests = array(); /** - * @var boolean + * Constructor. + * + * @param PHP_CodeCoverage_Driver $driver + * @param PHP_CodeCoverage_Filter $filter */ - protected $isCodeCoverageTestSuite = FALSE; + public function __construct(PHP_CodeCoverage_Driver $driver = NULL, PHP_CodeCoverage_Filter $filter = NULL) + { + if ($driver === NULL) { + $driver = new PHP_CodeCoverage_Driver_Xdebug; + } - /** - * @var boolean - */ - protected $isFileIteratorTestSuite = FALSE; + if ($filter === NULL) { + $filter = new PHP_CodeCoverage_Filter; + } + + $this->driver = $driver; + $this->filter = $filter; + } /** - * @var boolean + * 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 */ - protected $isTimerTestSuite = FALSE; + public function getReport() + { + $factory = new PHP_CodeCoverage_Report_Factory; + + return $factory->create($this); + } /** - * @var boolean + * Clears collected code coverage data. */ - protected $isTokenStreamTestSuite = FALSE; + public function clear() + { + $this->currentId = NULL; + $this->data = array(); + $this->tests = array(); + } /** - * Default PHP_CodeCoverage object. + * Returns the PHP_CodeCoverage_Filter used. * - * @var PHP_CodeCoverage + * @return PHP_CodeCoverage_Filter */ - protected static $instance; + public function filter() + { + return $this->filter; + } /** - * Constructor. + * Returns the collected code coverage data. * - * @param PHP_CodeCoverage_Driver $driver - * @param PHP_CodeCoverage_Filter $filter - * @throws InvalidArgumentException + * @return array + * @since Method available since Release 1.1.0 */ - public function __construct(PHP_CodeCoverage_Driver $driver = NULL, PHP_CodeCoverage_Filter $filter = NULL) + public function getData() { - if ($driver === NULL) { - $driver = new PHP_CodeCoverage_Driver_Xdebug; - } - - if ($filter === NULL) { - $filter = PHP_CodeCoverage_Filter::getInstance(); - } - - $this->driver = $driver; - $this->filter = $filter; - - if (defined('PHP_CODECOVERAGE_TESTSUITE')) { - $this->isCodeCoverageTestSuite = TRUE; + if ($this->addUncoveredFilesFromWhitelist) { + $this->addUncoveredFilesFromWhitelist(); } - if (defined('FILE_ITERATOR_TESTSUITE')) { - $this->isFileIteratorTestSuite = TRUE; + // We need to apply the blacklist filter a second time + // when no whitelist is used. + if (!$this->filter->hasWhitelist()) { + $this->applyListsFilter($this->data); } - if (defined('PHP_TIMER_TESTSUITE')) { - $this->isTimerTestSuite = TRUE; - } - - if (defined('PHP_TOKENSTREAM_TESTSUITE')) { - $this->isTokenStreamTestSuite = TRUE; - } + return $this->data; } /** - * Returns the default instance. + * Returns the test data. * - * @return PHP_CodeCoverage + * @return array + * @since Method available since Release 1.1.0 */ - public static function getInstance() + public function getTests() { - if (self::$instance === NULL) { - // @codeCoverageIgnoreStart - self::$instance = new PHP_CodeCoverage; - } - // @codeCoverageIgnoreEnd - return self::$instance; + return $this->tests; } /** @@ -203,12 +215,14 @@ public static function getInstance() * * @param mixed $id * @param boolean $clear - * @throws InvalidArgumentException + * @throws PHP_CodeCoverage_Exception */ public function start($id, $clear = FALSE) { if (!is_bool($clear)) { - throw new InvalidArgumentException; + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); } if ($clear) { @@ -225,19 +239,18 @@ public function start($id, $clear = FALSE) * * @param boolean $append * @return array - * @throws InvalidArgumentException + * @throws PHP_CodeCoverage_Exception */ public function stop($append = TRUE) { if (!is_bool($append)) { - throw new InvalidArgumentException; + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); } $data = $this->driver->stop(); - - if ($append) { - $this->append($data); - } + $this->append($data, NULL, $append); $this->currentId = NULL; @@ -247,178 +260,128 @@ public function stop($append = TRUE) /** * Appends code coverage data. * - * @param array $data - * @param mixed $id - * @param array $filterGroups + * @param array $data + * @param mixed $id + * @param boolean $append */ - public function append(array $data, $id = NULL, array $filterGroups = array('DEFAULT')) + public function append(array $data, $id = NULL, $append = TRUE) { if ($id === NULL) { $id = $this->currentId; } if ($id === NULL) { - throw new InvalidArgumentException; + throw new PHP_CodeCoverage_Exception; } - $this->applySelfFilter($data); - $this->applyListsFilter($data, $filterGroups); - $raw = $data; - $this->applyCoversAnnotationFilter($data, $id); + $this->applyListsFilter($data); + $this->initializeFilesThatAreSeenTheFirstTime($data); - if (!empty($data)) { - if ($id instanceof PHPUnit_Framework_TestCase) { - $status = $id->getStatus(); - $id = get_class($id) . '::' . $id->getName(); - $this->tests[$id] = $status; - } - - else if ($id instanceof PHPUnit_Extensions_PhptTestCase) { - $id = $id->getName(); - } + if (!$append) { + return; + } - $this->coveredFiles = array_unique( - array_merge($this->coveredFiles, array_keys($data)) - ); + if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') { + $this->applyCoversAnnotationFilter($data, $id); + } - $this->data[$id] = array('filtered' => $data, 'raw' => $raw); - $this->summary = array(); + if (empty($data)) { + return; } - } - /** - * Merges the data from another instance of PHP_CodeCoverage. - * - * @param PHP_CodeCoverage $that - */ - public function merge(PHP_CodeCoverage $that) - { - foreach ($that->data as $id => $data) { - if (!isset($this->data[$id])) { - $this->data[$id] = $data; - } else { - foreach (array('filtered', 'raw') as $type) { - foreach ($data[$type] as $file => $lines) { - if (!isset($this->data[$id][$type][$file])) { - $this->data[$id][$type][$file] = $lines; - } else { - foreach ($lines as $line => $flag) { - if (!isset($this->data[$id][$type][$file][$line]) || - $flag > $this->data[$id][$type][$file][$line]) { - $this->data[$id][$type][$file][$line] = $flag; - } - } - } - } - } - } + $status = NULL; + + if ($id instanceof PHPUnit_Framework_TestCase) { + $status = $id->getStatus(); + $id = get_class($id) . '::' . $id->getName(); } - foreach ($that->tests as $id => $status) { - if (!isset($this->tests[$id]) || $status > $this->tests[$id]) { - $this->tests[$id] = $status; - } + else if ($id instanceof PHPUnit_Extensions_PhptTestCase) { + $id = $id->getName(); } - $this->coveredFiles = array_unique( - array_merge($this->coveredFiles, $that->coveredFiles) - ); + $this->tests[$id] = $status; - $this->summary = array(); + foreach ($data as $file => $lines) { + if (!$this->filter->isFile($file)) { + continue; + } + + foreach ($lines as $k => $v) { + if ($v == 1) { + $this->data[$file][$k][] = $id; + } + } + } } /** - * Returns summarized code coverage data. - * - * Format of the result array: - * - * - * array( - * "/tested/code.php" => array( - * linenumber => array(tests that executed the line) - * ) - * ) - * + * Merges the data from another instance of PHP_CodeCoverage. * - * @return array + * @param PHP_CodeCoverage $that */ - public function getSummary() + public function merge(PHP_CodeCoverage $that) { - if (empty($this->summary)) { - if ($this->processUncoveredFilesFromWhitelist) { - $this->processUncoveredFilesFromWhitelist(); - } - - foreach ($this->data as $test => $coverage) { - foreach ($coverage['filtered'] as $file => $lines) { - foreach ($lines as $line => $flag) { - if ($flag == 1) { - if (!isset($this->summary[$file][$line][0])) { - $this->summary[$file][$line] = array(); - } - - if (isset($this->tests[$test])) { - $status = $this->tests[$test]; - } else { - $status = NULL; - } - - $this->summary[$file][$line][] = array( - 'id' => $test, 'status' => $status - ); - } - } + foreach ($that->data as $file => $lines) { + if (!isset($this->data[$file])) { + if (!$this->filter->isFiltered($file)) { + $this->data[$file] = $lines; } - foreach ($coverage['raw'] as $file => $lines) { - foreach ($lines as $line => $flag) { - if ($flag != 1 && - !isset($this->summary[$file][$line][0])) { - $this->summary[$file][$line] = $flag; - } - } - } + continue; } - foreach ($this->summary as &$file) { - ksort($file); + 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) + ); + } + } } - - ksort($this->summary); } - return $this->summary; + $this->tests = array_merge($this->tests, $that->getTests()); } /** - * Clears collected code coverage data. + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception + * @since Method available since Release 1.1.0 */ - public function clear() + public function setCacheTokens($flag) { - $this->data = array(); - $this->coveredFiles = array(); - $this->summary = array(); - $this->currentId = NULL; + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); + } + + $this->cacheTokens = $flag; } /** - * Returns the PHP_CodeCoverage_Filter used. - * - * @return PHP_CodeCoverage_Filter + * @param boolean $flag + * @since Method available since Release 1.1.0 */ - public function filter() + public function getCacheTokens() { - return $this->filter; + return $this->cacheTokens; } /** * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHP_CodeCoverage_Exception */ public function setForceCoversAnnotation($flag) { if (!is_bool($flag)) { - throw new InvalidArgumentException; + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); } $this->forceCoversAnnotation = $flag; @@ -426,12 +389,14 @@ public function setForceCoversAnnotation($flag) /** * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHP_CodeCoverage_Exception */ public function setMapTestClassNameToCoveredClassName($flag) { if (!is_bool($flag)) { - throw new InvalidArgumentException; + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); } $this->mapTestClassNameToCoveredClassName = $flag; @@ -439,77 +404,32 @@ public function setMapTestClassNameToCoveredClassName($flag) /** * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHP_CodeCoverage_Exception */ - public function setProcessUncoveredFilesFromWhitelist($flag) + public function setAddUncoveredFilesFromWhitelist($flag) { if (!is_bool($flag)) { - throw new InvalidArgumentException; + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); } - $this->processUncoveredFilesFromWhitelist = $flag; + $this->addUncoveredFilesFromWhitelist = $flag; } /** - * Filters sourcecode files from PHP_CodeCoverage, PHP_TokenStream, - * Text_Template, and File_Iterator. - * - * @param array $data + * @param boolean $flag + * @throws PHP_CodeCoverage_Exception */ - protected function applySelfFilter(&$data) + public function setProcessUncoveredFilesFromWhitelist($flag) { - foreach (array_keys($data) as $filename) { - if (!$this->filter->isFile($filename)) { - unset($data[$filename]); - continue; - } - - if (!$this->isCodeCoverageTestSuite && - strpos($filename, dirname(__FILE__)) === 0) { - unset($data[$filename]); - continue; - } - - if (!$this->isFileIteratorTestSuite && - (substr($filename, -17) == 'File/Iterator.php' || - substr($filename, -25) == 'File/Iterator/Factory.php')) { - unset($data[$filename]); - continue; - } - - if (!$this->isTimerTestSuite && - (substr($filename, -13) == 'PHP/Timer.php')) { - unset($data[$filename]); - continue; - } - - if (!$this->isTokenStreamTestSuite && - (substr($filename, -13) == 'PHP/Token.php' || - substr($filename, -20) == 'PHP/Token/Stream.php' || - substr($filename, -35) == 'PHP/Token/Stream/CachingFactory.php')) { - unset($data[$filename]); - continue; - } - - if (substr($filename, -17) == 'Text/Template.php') { - unset($data[$filename]); - } + if (!is_bool($flag)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'boolean' + ); } - } - /** - * Applies the blacklist/whitelist filtering. - * - * @param array $data - * @param array $filterGroups - */ - protected function applyListsFilter(&$data, $filterGroups) - { - foreach (array_keys($data) as $filename) { - if ($this->filter->isFiltered($filename, $filterGroups)) { - unset($data[$filename]); - } - } + $this->processUncoveredFilesFromWhitelist = $flag; } /** @@ -522,7 +442,7 @@ protected function applyCoversAnnotationFilter(&$data, $id) { if ($id instanceof PHPUnit_Framework_TestCase) { $testClassName = get_class($id); - $linesToBeCovered = PHP_CodeCoverage_Util::getLinesToBeCovered( + $linesToBeCovered = $this->getLinesToBeCovered( $testClassName, $id->getName() ); @@ -559,55 +479,318 @@ protected function applyCoversAnnotationFilter(&$data, $id) } } + /** + * 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 processUncoveredFilesFromWhitelist() + protected function addUncoveredFilesFromWhitelist() { $data = array(); - $includedFiles = array_flip(get_included_files()); $uncoveredFiles = array_diff( - $this->filter->getWhitelist(), $this->coveredFiles + $this->filter->getWhitelist(), array_keys($this->data) ); foreach ($uncoveredFiles as $uncoveredFile) { - if (isset($includedFiles[$uncoveredFile])) { - foreach (array_keys($this->data) as $test) { - if (isset($this->data[$test]['raw'][$uncoveredFile])) { - $coverage = $this->data[$test]['raw'][$uncoveredFile]; - - foreach (array_keys($coverage) as $key) { - if ($coverage[$key] == 1) { - $coverage[$key] = -1; - } - } + if (!file_exists($uncoveredFile)) { + continue; + } + + if ($this->processUncoveredFilesFromWhitelist) { + $this->processUncoveredFileFromWhitelist( + $uncoveredFile, $data, $uncoveredFiles + ); + } else { + $data[$uncoveredFile] = array(); - $data[$uncoveredFile] = $coverage; + $lines = count(file($uncoveredFile)); - break; + 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 { - $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; - } + $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 + ) + ); } - $data[$file] = $fileCoverage; - $includedFiles[$file] = TRUE; + $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); + } } - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + return $codeToCoverList; } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Autoload.php b/libs/PHPUnit/PHP/CodeCoverage/Autoload.php new file mode 100644 index 0000000..7bfa231 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Autoload.php.in b/libs/PHPUnit/PHP/CodeCoverage/Autoload.php.in new file mode 100644 index 0000000..dc34395 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Driver.php b/libs/PHPUnit/PHP/CodeCoverage/Driver.php index ad5801b..0ccdad7 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Driver.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Driver.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +49,8 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 */ diff --git a/libs/PHPUnit/PHP/CodeCoverage/Driver/Xdebug.php b/libs/PHPUnit/PHP/CodeCoverage/Driver/Xdebug.php index ceb81c1..a2704f8 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Driver/Xdebug.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Driver/Xdebug.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,41 +37,49 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/CodeCoverage/Driver.php'; - -if (version_compare(phpversion('xdebug'), '2.2.0-dev', '>=') && - !ini_get('xdebug.coverage_enable')) { - die("You need to set xdebug.coverage_enable=On in your php.ini.\n"); -} - /** * Driver for Xdebug's code coverage functionality. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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() { - // @codeCoverageIgnoreStart xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); - // @codeCoverageIgnoreEnd } /** @@ -81,11 +89,9 @@ public function start() */ public function stop() { - // @codeCoverageIgnoreStart $codeCoverage = xdebug_get_code_coverage(); xdebug_stop_code_coverage(); return $codeCoverage; - // @codeCoverageIgnoreEnd } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Exception.php b/libs/PHPUnit/PHP/CodeCoverage/Exception.php new file mode 100644 index 0000000..18cd14d --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Filter.php b/libs/PHPUnit/PHP/CodeCoverage/Filter.php index f37161d..d98f6a8 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Filter.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Filter.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,23 +37,20 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'File/Iterator/Factory.php'; - /** * Filter for blacklisting and whitelisting of code coverage information. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 */ @@ -64,9 +61,7 @@ class PHP_CodeCoverage_Filter * * @var array */ - protected $blacklistedFiles = array( - 'DEFAULT' => array() - ); + protected $blacklistedFiles = array(); /** * Source files that are whitelisted. @@ -76,27 +71,9 @@ class PHP_CodeCoverage_Filter protected $whitelistedFiles = array(); /** - * Default PHP_CodeCoverage object. - * - * @var PHP_CodeCoverage + * @var boolean */ - protected static $instance; - - /** - * Returns the default instance. - * - * @return PHP_CodeCoverage_Filter - */ - public static function getInstance() - { - if (self::$instance === NULL) { - // @codeCoverageIgnoreStart - self::$instance = new PHP_CodeCoverage_Filter; - } - // @codeCoverageIgnoreEnd - - return self::$instance; - } + protected $blacklistPrefilled = FALSE; /** * Adds a directory to the blacklist (recursively). @@ -104,16 +81,16 @@ public static function getInstance() * @param string $directory * @param string $suffix * @param string $prefix - * @param string $group */ - public function addDirectoryToBlacklist($directory, $suffix = '.php', $prefix = '', $group = 'DEFAULT') + public function addDirectoryToBlacklist($directory, $suffix = '.php', $prefix = '') { - $files = File_Iterator_Factory::getFileIterator( + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( $directory, $suffix, $prefix ); foreach ($files as $file) { - $this->addFileToBlacklist($file->getPathName(), $group, FALSE); + $this->addFileToBlacklist($file); } } @@ -121,23 +98,21 @@ public function addDirectoryToBlacklist($directory, $suffix = '.php', $prefix = * Adds a file to the blacklist. * * @param string $filename - * @param string $group */ - public function addFileToBlacklist($filename, $group = 'DEFAULT') + public function addFileToBlacklist($filename) { - $this->blacklistedFiles[$group][realpath($filename)] = TRUE; + $this->blacklistedFiles[realpath($filename)] = TRUE; } /** * Adds files to the blacklist. * - * @param array $files - * @param string $group + * @param array $files */ - public function addFilesToBlacklist(array $files, $group = 'DEFAULT') + public function addFilesToBlacklist(array $files) { foreach ($files as $file) { - $this->addFileToBlacklist($file, $group); + $this->addFileToBlacklist($file); } } @@ -147,16 +122,16 @@ public function addFilesToBlacklist(array $files, $group = 'DEFAULT') * @param string $directory * @param string $suffix * @param string $prefix - * @param string $group */ - public function removeDirectoryFromBlacklist($directory, $suffix = '.php', $prefix = '', $group = 'DEFAULT') + public function removeDirectoryFromBlacklist($directory, $suffix = '.php', $prefix = '') { - $files = File_Iterator_Factory::getFileIterator( + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( $directory, $suffix, $prefix ); foreach ($files as $file) { - $this->removeFileFromBlacklist($file->getPathName(), $group); + $this->removeFileFromBlacklist($file); } } @@ -164,14 +139,13 @@ public function removeDirectoryFromBlacklist($directory, $suffix = '.php', $pref * Removes a file from the blacklist. * * @param string $filename - * @param string $group */ - public function removeFileFromBlacklist($filename, $group = 'DEFAULT') + public function removeFileFromBlacklist($filename) { $filename = realpath($filename); - if (isset($this->blacklistedFiles[$group][$filename])) { - unset($this->blacklistedFiles[$group][$filename]); + if (isset($this->blacklistedFiles[$filename])) { + unset($this->blacklistedFiles[$filename]); } } @@ -184,21 +158,19 @@ public function removeFileFromBlacklist($filename, $group = 'DEFAULT') */ public function addDirectoryToWhitelist($directory, $suffix = '.php', $prefix = '') { - $files = File_Iterator_Factory::getFileIterator( + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( $directory, $suffix, $prefix ); foreach ($files as $file) { - $this->addFileToWhitelist($file->getPathName(), FALSE); + $this->addFileToWhitelist($file); } } /** * Adds a file to the whitelist. * - * When the whitelist is empty (default), blacklisting is used. - * When the whitelist is not empty, whitelisting is used. - * * @param string $filename */ public function addFileToWhitelist($filename) @@ -227,12 +199,13 @@ public function addFilesToWhitelist(array $files) */ public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '') { - $files = File_Iterator_Factory::getFileIterator( + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( $directory, $suffix, $prefix ); foreach ($files as $file) { - $this->removeFileFromWhitelist($file->getPathName()); + $this->removeFileFromWhitelist($file); } } @@ -255,11 +228,12 @@ public function removeFileFromWhitelist($filename) * * @param string $filename */ - public static function isFile($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; @@ -275,35 +249,23 @@ public static function isFile($filename) * When the whitelist is not empty, whitelisting is used. * * @param string $filename - * @param array $groups * @param boolean $ignoreWhitelist * @return boolean - * @throws InvalidArgumentException + * @throws PHP_CodeCoverage_Exception */ - public function isFiltered($filename, array $groups = array('DEFAULT'), $ignoreWhitelist = FALSE) + public function isFiltered($filename) { - if (!is_bool($ignoreWhitelist)) { - throw new InvalidArgumentException; - } - $filename = realpath($filename); - if (!$ignoreWhitelist && !empty($this->whitelistedFiles)) { + if (!empty($this->whitelistedFiles)) { return !isset($this->whitelistedFiles[$filename]); } - $blacklistedFiles = array(); - - foreach ($groups as $group) { - if (isset($this->blacklistedFiles[$group])) { - $blacklistedFiles = array_merge( - $blacklistedFiles, - $this->blacklistedFiles[$group] - ); - } + if (!$this->blacklistPrefilled) { + $this->prefillBlacklist(); } - return isset($blacklistedFiles[$filename]); + return isset($this->blacklistedFiles[$filename]); } /** @@ -313,13 +275,7 @@ public function isFiltered($filename, array $groups = array('DEFAULT'), $ignoreW */ public function getBlacklist() { - $blacklistedFiles = array(); - - foreach ($this->blacklistedFiles as $group => $list) { - $blacklistedFiles[$group] = array_keys($list); - } - - return $blacklistedFiles; + return array_keys($this->blacklistedFiles); } /** @@ -331,4 +287,57 @@ 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/libs/PHPUnit/PHP/CodeCoverage/Report/Clover.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Clover.php index 7f05708..050c622 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/Clover.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Report/Clover.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,24 +37,20 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/CodeCoverage.php'; -require_once 'PHP/Token/Stream/CachingFactory.php'; - /** * Generates a Clover XML logfile from an PHP_CodeCoverage object. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 */ @@ -68,400 +64,283 @@ class PHP_CodeCoverage_Report_Clover */ public function process(PHP_CodeCoverage $coverage, $target = NULL, $name = NULL) { - $document = new DOMDocument('1.0', 'UTF-8'); - $document->formatOutput = TRUE; + $xmlDocument = new DOMDocument('1.0', 'UTF-8'); + $xmlDocument->formatOutput = TRUE; - $root = $document->createElement('coverage'); - $root->setAttribute('generated', (int)$_SERVER['REQUEST_TIME']); - $document->appendChild($root); + $xmlCoverage = $xmlDocument->createElement('coverage'); + $xmlCoverage->setAttribute('generated', (int)$_SERVER['REQUEST_TIME']); + $xmlDocument->appendChild($xmlCoverage); - $project = $document->createElement('project'); - $project->setAttribute('timestamp', (int)$_SERVER['REQUEST_TIME']); + $xmlProject = $xmlDocument->createElement('project'); + $xmlProject->setAttribute('timestamp', (int)$_SERVER['REQUEST_TIME']); if (is_string($name)) { - $project->setAttribute('name', $name); + $xmlProject->setAttribute('name', $name); } - $root->appendChild($project); + $xmlCoverage->appendChild($xmlProject); - $files = $coverage->getSummary(); $packages = array(); + $report = $coverage->getReport(); + unset($coverage); - $projectStatistics = array( - 'files' => 0, - 'loc' => 0, - 'ncloc' => 0, - 'classes' => 0, - 'methods' => 0, - 'coveredMethods' => 0, - 'conditionals' => 0, - 'coveredConditionals' => 0, - 'statements' => 0, - 'coveredStatements' => 0 - ); - - foreach ($files as $filename => $data) { + foreach ($report as $item) { $namespace = 'global'; - if (file_exists($filename)) { - $fileStatistics = array( - 'classes' => 0, - 'methods' => 0, - 'coveredMethods' => 0, - 'conditionals' => 0, - 'coveredConditionals' => 0, - 'statements' => 0, - 'coveredStatements' => 0 - ); - - $file = $document->createElement('file'); - $file->setAttribute('name', $filename); - - $tokens = PHP_Token_Stream_CachingFactory::get($filename); - $classesInFile = $tokens->getClasses(); - $linesOfCode = $tokens->getLinesOfCode(); - unset($tokens); - - $ignoredLines = PHP_CodeCoverage_Util::getLinesToBeIgnored( - $filename - ); - - $lines = array(); - - foreach ($classesInFile as $className => $_class) { - $classStatistics = array( - 'methods' => 0, - 'coveredMethods' => 0, - 'conditionals' => 0, - 'coveredConditionals' => 0, - 'statements' => 0, - 'coveredStatements' => 0 - ); - - foreach ($_class['methods'] as $methodName => $method) { - $classStatistics['methods']++; - - $methodCount = 0; - $methodLines = 0; - $methodLinesCovered = 0; - - for ($i = $method['startLine']; - $i <= $method['endLine']; - $i++) { - if (isset($ignoredLines[$i])) { - continue; - } - - $add = TRUE; - $count = 0; + if (!$item instanceof PHP_CodeCoverage_Report_Node_File) { + continue; + } - if (isset($files[$filename][$i])) { - if ($files[$filename][$i] != -2) { - $classStatistics['statements']++; - $methodLines++; - } + $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; + } - if (is_array($files[$filename][$i])) { - $classStatistics['coveredStatements']++; - $methodLinesCovered++; - $count = count($files[$filename][$i]); - } + $add = TRUE; + $count = 0; - else if ($files[$filename][$i] == -2) { - $add = FALSE; - } + if (isset($coverage[$i])) { + if ($coverage[$i] !== NULL) { + $classStatements++; + $methodLines++; } else { $add = FALSE; } - $methodCount = max($methodCount, $count); + $count = count($coverage[$i]); - if ($add) { - $lines[$i] = array( - 'count' => $count, - 'type' => 'stmt' - ); + if ($count > 0) { + $coveredClassStatements++; + $methodLinesCovered++; } + } else { + $add = FALSE; } - if ($methodCount > 0) { - $classStatistics['coveredMethods']++; - } - - $lines[$method['startLine']] = array( - 'count' => $methodCount, - 'crap' => PHP_CodeCoverage_Util::crap( - $method['ccn'], - PHP_CodeCoverage_Util::percent( - $methodLinesCovered, - $methodLines - ) - ), - 'type' => 'method', - 'name' => $methodName - ); - } - - $package = PHP_CodeCoverage_Util::getPackageInformation( - $className, $_class['docblock'] - ); + $methodCount = max($methodCount, $count); - if (!empty($package['namespace'])) { - $namespace = $package['namespace']; - } - - $class = $document->createElement('class'); - $class->setAttribute('name', $className); - $class->setAttribute('namespace', $namespace); - - if (!empty($package['fullPackage'])) { - $class->setAttribute( - 'fullPackage', $package['fullPackage'] - ); - } - - if (!empty($package['category'])) { - $class->setAttribute( - 'category', $package['category'] - ); - } - - if (!empty($package['package'])) { - $class->setAttribute( - 'package', $package['package'] - ); + if ($add) { + $lines[$i] = array( + 'count' => $count, 'type' => 'stmt' + ); + } } - if (!empty($package['subpackage'])) { - $class->setAttribute( - 'subpackage', $package['subpackage'] - ); + if ($methodCount > 0) { + $coveredMethods++; } - $file->appendChild($class); - - $metrics = $document->createElement('metrics'); - - $metrics->setAttribute( - 'methods', $classStatistics['methods'] - ); - - $metrics->setAttribute( - 'coveredmethods', $classStatistics['coveredMethods'] - ); - - $metrics->setAttribute( - 'conditionals', $classStatistics['conditionals'] - ); - - $metrics->setAttribute( - 'coveredconditionals', - $classStatistics['coveredConditionals'] + $lines[$method['startLine']] = array( + 'count' => $methodCount, + 'crap' => $method['crap'], + 'type' => 'method', + 'name' => $methodName ); + } - $metrics->setAttribute( - 'statements', $classStatistics['statements'] - ); + if (!empty($class['package']['namespace'])) { + $namespace = $class['package']['namespace']; + } - $metrics->setAttribute( - 'coveredstatements', - $classStatistics['coveredStatements'] - ); + $xmlClass = $xmlDocument->createElement('class'); + $xmlClass->setAttribute('name', $className); + $xmlClass->setAttribute('namespace', $namespace); - $metrics->setAttribute( - 'elements', - $classStatistics['conditionals'] + - $classStatistics['statements'] + - $classStatistics['methods'] + if (!empty($class['package']['fullPackage'])) { + $xmlClass->setAttribute( + 'fullPackage', $class['package']['fullPackage'] ); + } - $metrics->setAttribute( - 'coveredelements', - $classStatistics['coveredConditionals'] + - $classStatistics['coveredStatements'] + - $classStatistics['coveredMethods'] + if (!empty($class['package']['category'])) { + $xmlClass->setAttribute( + 'category', $class['package']['category'] ); - - $class->appendChild($metrics); - - $fileStatistics['methods'] += $classStatistics['methods']; - $fileStatistics['coveredMethods'] += $classStatistics['coveredMethods']; - $fileStatistics['conditionals'] += $classStatistics['conditionals']; - $fileStatistics['coveredConditionals'] += $classStatistics['coveredConditionals']; - $fileStatistics['statements'] += $classStatistics['statements']; - $fileStatistics['coveredStatements'] += $classStatistics['coveredStatements']; - $fileStatistics['classes']++; } - foreach ($data as $_line => $_data) { - if (isset($lines[$_line]) || isset($ignoredLines[$_line])) { - continue; - } - - if ($_data != -2) { - $fileStatistics['statements']++; - - if (is_array($_data)) { - $count = count($_data); - $fileStatistics['coveredStatements']++; - } else { - $count = 0; - } - - $lines[$_line] = array( - 'count' => $count, - 'type' => 'stmt' - ); - } + if (!empty($class['package']['package'])) { + $xmlClass->setAttribute( + 'package', $class['package']['package'] + ); } - ksort($lines); - - foreach ($lines as $_line => $_data) { - if (isset($ignoredLines[$_line])) { - continue; - } - - $line = $document->createElement('line'); - $line->setAttribute('num', $_line); - $line->setAttribute('type', $_data['type']); - - if (isset($_data['name'])) { - $line->setAttribute('name', $_data['name']); - } - - if (isset($_data['crap'])) { - $line->setAttribute('crap', $_data['crap']); - } - - $line->setAttribute('count', $_data['count']); - - $file->appendChild($line); + if (!empty($class['package']['subpackage'])) { + $xmlClass->setAttribute( + 'subpackage', $class['package']['subpackage'] + ); } - $metrics = $document->createElement('metrics'); + $xmlFile->appendChild($xmlClass); - $metrics->setAttribute('loc', $linesOfCode['loc']); - $metrics->setAttribute('ncloc', $linesOfCode['ncloc']); - $metrics->setAttribute('classes', $fileStatistics['classes']); - $metrics->setAttribute('methods', $fileStatistics['methods']); - - $metrics->setAttribute( - 'coveredmethods', $fileStatistics['coveredMethods'] + $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 ); - - $metrics->setAttribute( - 'conditionals', $fileStatistics['conditionals'] + $xmlMetrics->setAttribute( + 'elements', + count($class['methods']) + + $classStatements + /* + conditionals */); + $xmlMetrics->setAttribute( + 'coveredelements', + $coveredMethods + + $coveredClassStatements + /* + coveredconditionals */ ); + $xmlClass->appendChild($xmlMetrics); + } - $metrics->setAttribute( - 'coveredconditionals', $fileStatistics['coveredConditionals'] - ); + foreach ($coverage as $line => $data) { + if ($data === NULL || + isset($lines[$line]) || + isset($ignoredLines[$line])) { + continue; + } - $metrics->setAttribute( - 'statements', $fileStatistics['statements'] + $lines[$line] = array( + 'count' => count($data), 'type' => 'stmt' ); + } - $metrics->setAttribute( - 'coveredstatements', $fileStatistics['coveredStatements'] - ); + ksort($lines); - $metrics->setAttribute( - 'elements', - $fileStatistics['conditionals'] + - $fileStatistics['statements'] + - $fileStatistics['methods'] - ); + foreach ($lines as $line => $data) { + if (isset($ignoredLines[$line])) { + continue; + } - $metrics->setAttribute( - 'coveredelements', - $fileStatistics['coveredConditionals'] + - $fileStatistics['coveredStatements'] + - $fileStatistics['coveredMethods'] - ); + $xmlLine = $xmlDocument->createElement('line'); + $xmlLine->setAttribute('num', $line); + $xmlLine->setAttribute('type', $data['type']); - $file->appendChild($metrics); + if (isset($data['name'])) { + $xmlLine->setAttribute('name', $data['name']); + } - if ($namespace == 'global') { - $project->appendChild($file); - } else { - if (!isset($packages[$namespace])) { - $packages[$namespace] = $document->createElement( - 'package' - ); + if (isset($data['crap'])) { + $xmlLine->setAttribute('crap', $data['crap']); + } - $packages[$namespace]->setAttribute('name', $namespace); - $project->appendChild($packages[$namespace]); - } + $xmlLine->setAttribute('count', $data['count']); + $xmlFile->appendChild($xmlLine); + } - $packages[$namespace]->appendChild($file); + $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]); } - $projectStatistics['loc'] += $linesOfCode['loc']; - $projectStatistics['ncloc'] += $linesOfCode['ncloc']; - $projectStatistics['classes'] += $fileStatistics['classes']; - $projectStatistics['methods'] += $fileStatistics['methods']; - $projectStatistics['coveredMethods'] += $fileStatistics['coveredMethods']; - $projectStatistics['conditionals'] += $fileStatistics['conditionals']; - $projectStatistics['coveredConditionals'] += $fileStatistics['coveredConditionals']; - $projectStatistics['statements'] += $fileStatistics['statements']; - $projectStatistics['coveredStatements'] += $fileStatistics['coveredStatements']; - $projectStatistics['files']++; + $packages[$namespace]->appendChild($xmlFile); } } - $metrics = $document->createElement('metrics'); - - $metrics->setAttribute('files', $projectStatistics['files']); - $metrics->setAttribute('loc', $projectStatistics['loc']); - $metrics->setAttribute('ncloc', $projectStatistics['ncloc']); - $metrics->setAttribute('classes', $projectStatistics['classes']); - $metrics->setAttribute('methods', $projectStatistics['methods']); + $linesOfCode = $report->getLinesOfCode(); - $metrics->setAttribute( - 'coveredmethods', $projectStatistics['coveredMethods'] + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('files', count($report)); + $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); + $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); + $xmlMetrics->setAttribute( + 'classes', $report->getNumClassesAndTraits() ); - - $metrics->setAttribute( - 'conditionals', $projectStatistics['conditionals'] + $xmlMetrics->setAttribute('methods', $report->getNumMethods()); + $xmlMetrics->setAttribute( + 'coveredmethods', $report->getNumTestedMethods() ); - - $metrics->setAttribute( - 'coveredconditionals', $projectStatistics['coveredConditionals'] + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute( + 'statements', $report->getNumExecutableLines() ); - - $metrics->setAttribute( - 'statements', $projectStatistics['statements'] + $xmlMetrics->setAttribute( + 'coveredstatements', $report->getNumExecutedLines() ); - - $metrics->setAttribute( - 'coveredstatements', $projectStatistics['coveredStatements'] - ); - - $metrics->setAttribute( + $xmlMetrics->setAttribute( 'elements', - $projectStatistics['conditionals'] + - $projectStatistics['statements'] + - $projectStatistics['methods'] + $report->getNumMethods() + + $report->getNumExecutableLines() + /* + conditionals */ ); - - $metrics->setAttribute( + $xmlMetrics->setAttribute( 'coveredelements', - $projectStatistics['coveredConditionals'] + - $projectStatistics['coveredStatements'] + - $projectStatistics['coveredMethods'] + $report->getNumTestedMethods() + + $report->getNumExecutedLines() + /* + coveredconditionals */ ); - - $project->appendChild($metrics); + $xmlProject->appendChild($xmlMetrics); if ($target !== NULL) { if (!is_dir(dirname($target))) { - mkdir(dirname($target), 0777, TRUE); + mkdir(dirname($target), 0777, TRUE); } - return $document->save($target); + return $xmlDocument->save($target); } else { - return $document->saveXML(); + return $xmlDocument->saveXML(); } } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/Factory.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Factory.php new file mode 100644 index 0000000..e69c457 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML.php index 4313375..5db8731 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,25 +37,20 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/CodeCoverage.php'; -require_once 'PHP/CodeCoverage/Report/HTML/Node.php'; -require_once 'Text/Template.php'; - /** * Generates an HTML report from an PHP_CodeCoverage object. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 */ @@ -64,56 +59,53 @@ class PHP_CodeCoverage_Report_HTML /** * @var string */ - public static $templatePath; + protected $templatePath; /** - * @var array + * @var string */ - protected $options; + protected $charset; /** - * Constructor. - * - * @param array $options + * @var string */ - public function __construct(array $options = array()) - { - if (!isset($options['title'])) { - $options['title'] = ''; - } - - if (!isset($options['charset'])) { - $options['charset'] = 'UTF-8'; - } - - if (!isset($options['yui'])) { - $options['yui'] = TRUE; - } - - if (!isset($options['highlight'])) { - $options['highlight'] = FALSE; - } + protected $generator; - if (!isset($options['lowUpperBound'])) { - $options['lowUpperBound'] = 35; - } + /** + * @var integer + */ + protected $lowUpperBound; - if (!isset($options['highLowerBound'])) { - $options['highLowerBound'] = 70; - } + /** + * @var integer + */ + protected $highLowerBound; - if (!isset($options['generator'])) { - $options['generator'] = ''; - } + /** + * @var boolean + */ + protected $highlight; - $this->options = $options; + /** + * 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; - self::$templatePath = sprintf( - '%s%sHTML%sTemplate%s', + $this->templatePath = sprintf( + '%s%sHTML%sRenderer%sTemplate%s', dirname(__FILE__), DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR ); } @@ -124,185 +116,59 @@ public function __construct(array $options = array()) */ public function process(PHP_CodeCoverage $coverage, $target) { - $target = PHP_CodeCoverage_Util::getDirectory($target); - $files = $coverage->getSummary(); - $commonPath = PHP_CodeCoverage_Util::reducePaths($files); - $items = PHP_CodeCoverage_Util::buildDirectoryStructure($files); - $root = new PHP_CodeCoverage_Report_HTML_Node_Directory( - $commonPath, NULL - ); + $target = $this->getDirectory($target); + $report = $coverage->getReport(); + unset($coverage); - $this->addItems($root, $items); - - $this->renderDashboard( - $root, $target . 'index.dashboard.html', $this->options['title'] - ); - - foreach ($root as $node) { - if ($node instanceof PHP_CodeCoverage_Report_HTML_Node_Directory) { - $this->renderDashboard( - $node, - $target . PHP_CodeCoverage_Util::getSafeFilename( - $node->getId() - ) . '.dashboard.html', - $node->getName(TRUE) - ); - } + if (!isset($_SERVER['REQUEST_TIME'])) { + $_SERVER['REQUEST_TIME'] = time(); } - $root->render( - $target, - $this->options['title'], - $this->options['charset'], - $this->options['lowUpperBound'], - $this->options['highLowerBound'], - $this->options['generator'] - ); - - $this->copyFiles($target); - } + $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); - /** - * @param PHP_CodeCoverage_Report_HTML_Node_Directory $root - * @param string $file - * @param string $title - */ - protected function renderDashboard(PHP_CodeCoverage_Report_HTML_Node_Directory $root, $file, $title) - { - $classes = $this->classes($root); - $template = new Text_Template( - PHP_CodeCoverage_Report_HTML::$templatePath . 'dashboard.html' + $dashboard = new PHP_CodeCoverage_Report_HTML_Renderer_Dashboard( + $this->templatePath, + $this->charset, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound ); - $template->setVar( - array( - 'title' => $title, - 'charset' => $this->options['charset'], - 'date' => date( - 'D M j G:i:s T Y', - $_SERVER['REQUEST_TIME'] - ), - 'version' => '1.0.4', - 'php_version' => PHP_VERSION, - 'generator' => $this->options['generator'], - '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)) - ) + $directory = new PHP_CodeCoverage_Report_HTML_Renderer_Directory( + $this->templatePath, + $this->charset, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound ); - $template->renderTo($file); - } - - /** - * @param PHP_CodeCoverage_Report_HTML_Node_Directory $root - * @param array $items - */ - protected function addItems(PHP_CodeCoverage_Report_HTML_Node_Directory $root, array $items) - { - foreach ($items as $key => $value) { - if (substr($key, -2) == '/f') { - try { - $root->addFile( - substr($key, 0, -2), - $value, - $this->options['yui'], - $this->options['highlight'] - ); - } - - catch (RuntimeException $e) { - continue; - } - } else { - $child = $root->addDirectory($key); - $this->addItems($child, $value); - } - } - } - - /** - * Returns the classes. - * - * @param PHP_CodeCoverage_Report_HTML_Node_Directory $root - * @return array - */ - protected function classes(PHP_CodeCoverage_Report_HTML_Node_Directory $root) - { - $classes = array(); - - foreach ($root as $node) { - if ($node instanceof PHP_CodeCoverage_Report_HTML_Node_File) { - $classes = array_merge($classes, $node->getClasses()); - } - } - - if (isset($classes['*'])) { - unset($classes['*']); - } - - return $classes; - } - - /** - * 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'], 'blue', $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 + $file = new PHP_CodeCoverage_Report_HTML_Renderer_File( + $this->templatePath, + $this->charset, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound, + $this->highlight ); - foreach ($classes as $class) { - if ($class['coverage'] == 0) { - $data['0%']++; - } + $dashboard->render($report, $target . 'index.dashboard.html'); + $directory->render($report, $target . 'index.html'); - else if ($class['coverage'] == 100) { - $data['100%']++; - } + foreach ($report as $node) { + $id = $node->getId(); - else { - $key = floor($class['coverage']/10)*10; - $key = $key . '-' . ($key + 10) . '%'; - $data[$key]++; + 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'); } } - return json_encode(array_values($data)); + $this->copyFiles($target); } /** @@ -310,110 +176,46 @@ protected function classCoverageDistribution(array $classes) */ protected function copyFiles($target) { - $files = array( - 'butter.png', - 'chameleon.png', - 'close12_1.gif', - 'container.css', - 'container-min.js', - 'directory.png', - 'excanvas.compressed.js', - 'file.png', - 'glass.png', - 'RGraph.bar.js', - 'RGraph.common.core.js', - 'RGraph.common.tooltips.js', - 'RGraph.scatter.js', - 'scarlet_red.png', - 'snow.png', - 'style.css', - 'yahoo-dom-event.js' - ); - - foreach ($files as $file) { - copy(self::$templatePath . $file, $target . $file); - } + $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'); } /** - * Returns the least tested methods. - * - * @param array $classes - * @param integer $max + * @param string $directory * @return string + * @throws PHP_CodeCoverage_Exception + * @since Method available since Release 1.2.0 */ - protected function leastTestedMethods(array $classes, $max = 10) + protected function getDirectory($directory) { - $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']; - } - } + if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { + $directory .= DIRECTORY_SEPARATOR; } - 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]['file'], - $name, - $coverage - ); + if (is_dir($directory)) { + return $directory; } - 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']; - } + if (mkdir($directory, 0777, TRUE)) { + return $directory; } - asort($risks); - - $risks = array_reverse( - array_slice($risks, 0, min($max, count($risks))) + throw new PHP_CodeCoverage_Exception( + sprintf( + 'Directory "%s" does not exist.', + $directory + ) ); - - $buffer = ''; - - foreach ($risks as $name => $crap) { - $buffer .= sprintf( - '
  • %s (%d)
  • ' . "\n", - $classes[$name]['file'], - $name, - $crap - ); - } - - return $buffer; } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node.php deleted file mode 100644 index d7d0e45..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node.php +++ /dev/null @@ -1,512 +0,0 @@ -. - * 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-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -/** - * Base class for nodes in the code coverage information tree. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -abstract class PHP_CodeCoverage_Report_HTML_Node -{ - /** - * @var array - */ - protected $cache = array(); - - /** - * @var string - */ - protected $name; - - /** - * @var PHP_CodeCoverage_Report_HTML_Node - */ - protected $parent; - - /** - * Constructor. - * - * @param string $name - * @param PHP_CodeCoverage_Report_HTML_Node $parent - */ - public function __construct($name, PHP_CodeCoverage_Report_HTML_Node $parent = NULL) - { - $this->name = $name; - $this->parent = $parent; - } - - /** - * Returns the percentage of classes that has been tested. - * - * @return integer - */ - public function getTestedClassesPercent() - { - return PHP_CodeCoverage_Util::percent( - $this->getNumTestedClasses(), - $this->getNumClasses(), - TRUE - ); - } - - /** - * Returns the percentage of methods that has been tested. - * - * @return integer - */ - public function getTestedMethodsPercent() - { - return PHP_CodeCoverage_Util::percent( - $this->getNumTestedMethods(), - $this->getNumMethods(), - TRUE - ); - } - - /** - * Returns the percentage of executed lines. - * - * @return integer - */ - public function getLineExecutedPercent() - { - return PHP_CodeCoverage_Util::percent( - $this->getNumExecutedLines(), - $this->getNumExecutableLines(), - TRUE - ); - } - - /** - * Returns this node's ID. - * - * @return string - */ - public function getId() - { - if (!isset($this->cache['id'])) { - if ($this->parent === NULL) { - $this->cache['id'] = 'index'; - } else { - $parentId = $this->parent->getId(); - - if ($parentId == 'index') { - $this->cache['id'] = $this->getName(); - } else { - $this->cache['id'] = $parentId . '_' . $this->getName(); - } - } - } - - return $this->cache['id']; - } - - /** - * Returns this node's name. - * - * @param boolean $includeParent - * @return string - */ - public function getName($includeParent = FALSE, $includeCommonPath = FALSE) - { - if ($includeParent && $this->parent !== NULL) { - if (!isset($this->cache['nameIncludingParent'])) { - $parent = $this->parent->getName(TRUE); - - if (!empty($parent)) { - $this->cache['nameIncludingParent'] = $parent . '/' . - $this->name; - } else { - $this->cache['nameIncludingParent'] = $this->name; - } - } - - return $this->cache['nameIncludingParent']; - } else { - if ($this->parent !== NULL) { - return $this->name; - } else { - return $includeCommonPath ? $this->name : ''; - } - } - } - - /** - * Returns the link to this node. - * - * @param boolean $full - * @return string - */ - public function getLink($full) - { - if (substr($this->name, -1) == DIRECTORY_SEPARATOR) { - $name = substr($this->name, 0, -1); - } else { - $name = $this->name; - } - - $cleanId = PHP_CodeCoverage_Util::getSafeFilename($this->getId()); - - if ($full) { - if ($this->parent !== NULL) { - $parent = $this->parent->getLink(TRUE) . DIRECTORY_SEPARATOR; - } else { - $parent = ''; - } - - return sprintf( - '%s%s', - $parent, - $cleanId, - $name - ); - } else { - return sprintf( - '%s', - $cleanId, - $name - ); - } - } - - /** - * Returns this node's path. - * - * @return string - */ - public function getPath() - { - if (!isset($this->cache['path'])) { - if ($this->parent === NULL) { - $this->cache['path'] = $this->getName(FALSE, TRUE); - } else { - $parentPath = $this->parent->getPath(); - - if (substr($parentPath, -1) == DIRECTORY_SEPARATOR) { - $this->cache['path'] = $parentPath . - $this->getName(FALSE, TRUE); - } else { - $this->cache['path'] = $parentPath . - DIRECTORY_SEPARATOR . - $this->getName(FALSE, TRUE); - - if ($parentPath === '' && - realpath($this->cache['path']) === FALSE && - realpath($this->getName(FALSE, TRUE)) !== FALSE) { - $this->cache['path'] = $this->getName(FALSE, TRUE); - } - } - } - } - - return $this->cache['path']; - } - - protected function doRenderItemObject(PHP_CodeCoverage_Report_HTML_Node $item, $lowUpperBound, $highLowerBound, $link = NULL, $itemClass = 'coverItem') - { - return $this->doRenderItem( - array( - 'name' => $link != NULL ? $link : $item->getLink( - FALSE - ), - 'itemClass' => $itemClass, - 'numClasses' => $item->getNumClasses(), - 'numTestedClasses' => $item->getNumTestedClasses(), - 'testedClassesPercent' => $item->getTestedClassesPercent(), - 'numMethods' => $item->getNumMethods(), - 'numTestedMethods' => $item->getNumTestedMethods(), - 'testedMethodsPercent' => $item->getTestedMethodsPercent(), - 'numExecutableLines' => $item->getNumExecutableLines(), - 'numExecutedLines' => $item->getNumExecutedLines(), - 'executedLinesPercent' => $item->getLineExecutedPercent(), - 'crap' => $link == 'Total' ? 'CRAP' : '' - ), - $lowUpperBound, - $highLowerBound - ); - } - - protected function doRenderItem(array $data, $lowUpperBound, $highLowerBound, $template = NULL) - { - if ($template === NULL) { - if ($this instanceof PHP_CodeCoverage_Report_HTML_Node_Directory) { - $template = 'directory_item.html'; - } else { - $template = 'file_item.html'; - } - } - - $itemTemplate = new Text_Template( - PHP_CodeCoverage_Report_HTML::$templatePath . $template - ); - - if ($data['numClasses'] > 0) { - list($classesColor, $classesLevel) = $this->getColorLevel( - $data['testedClassesPercent'], $lowUpperBound, $highLowerBound - ); - - $classesNumber = $data['numTestedClasses'] . ' / ' . - $data['numClasses']; - } else { - $classesColor = 'snow'; - $classesLevel = 'None'; - $classesNumber = ' '; - } - - if ($data['numMethods'] > 0) { - list($methodsColor, $methodsLevel) = $this->getColorLevel( - $data['testedMethodsPercent'], $lowUpperBound, $highLowerBound - ); - - $methodsNumber = $data['numTestedMethods'] . ' / ' . - $data['numMethods']; - } else { - $methodsColor = 'snow'; - $methodsLevel = 'None'; - $methodsNumber = ' '; - } - - list($linesColor, $linesLevel) = $this->getColorLevel( - $data['executedLinesPercent'], $lowUpperBound, $highLowerBound - ); - - if ($data['name'] == '*') { - $functions = TRUE; - } else { - $functions = FALSE; - } - - $icon = ''; - - if (isset($data['itemClass'])) { - if ($data['itemClass'] == 'coverDirectory') { - $icon = 'directory '; - } - - else if ($data['itemClass'] == 'coverFile') { - $icon = 'file '; - } - } - - $itemTemplate->setVar( - array( - 'name' => $functions ? 'Functions' : $data['name'], - 'icon' => $icon, - 'itemClass' => isset($data['itemClass']) ? $data['itemClass'] : 'coverItem', - 'classes_color' => $classesColor, - 'classes_level' => $functions ? 'None' : $classesLevel, - 'classes_tested_width' => floor($data['testedClassesPercent']), - 'classes_tested_percent' => !$functions && $data['numClasses'] > 0 ? $data['testedClassesPercent'] . '%' : ' ', - 'classes_not_tested_width' => 100 - floor($data['testedClassesPercent']), - 'classes_number' => $functions ? ' ' : $classesNumber, - 'methods_color' => $methodsColor, - 'methods_level' => $methodsLevel, - 'methods_tested_width' => floor($data['testedMethodsPercent']), - 'methods_tested_percent' => $data['numMethods'] > 0 ? $data['testedMethodsPercent'] . '%' : ' ', - 'methods_not_tested_width' => 100 - floor($data['testedMethodsPercent']), - 'methods_number' => $methodsNumber, - 'lines_color' => $linesColor, - 'lines_level' => $linesLevel, - 'lines_executed_width' => floor($data['executedLinesPercent']), - 'lines_executed_percent' => $data['executedLinesPercent'] . '%', - 'lines_not_executed_width' => 100 - floor($data['executedLinesPercent']), - 'num_executable_lines' => $data['numExecutableLines'], - 'num_executed_lines' => $data['numExecutedLines'], - 'crap' => isset($data['crap']) ? $data['crap'] : '' - ) - ); - - return $itemTemplate->render(); - } - - protected function getColorLevel($percent, $lowUpperBound, $highLowerBound) - { - $floorPercent = floor($percent); - - if ($floorPercent < $lowUpperBound) { - $color = 'scarlet_red'; - $level = 'Lo'; - } - - else if ($floorPercent >= $lowUpperBound && - $floorPercent < $highLowerBound) { - $color = 'butter'; - $level = 'Med'; - } - - else { - $color = 'chameleon'; - $level = 'Hi'; - } - - return array($color, $level); - } - - protected function renderTotalItem($lowUpperBound, $highLowerBound, $directory = TRUE) - { - if ($directory && - empty($this->directories) && - count($this->files) == 1) { - return ''; - } - - return $this->doRenderItemObject( - $this, $lowUpperBound, $highLowerBound, 'Total' - ) . - " \n" . - '  ' . - "\n \n"; - } - - /** - * @param Text_Template $template - * @param string $title - * @param string $charset - * @param string $generator - */ - protected function setTemplateVars(Text_Template $template, $title, $charset, $generator) - { - $dashboard = ''; - - if ($this instanceof PHP_CodeCoverage_Report_HTML_Node_Directory) { - $dashboard = sprintf( - 'dashboard', - PHP_CodeCoverage_Util::getSafeFilename( - $this->getId() - ) . '.dashboard.html' - ); - } - - $template->setVar( - array( - 'title' => $title, - 'charset' => $charset, - 'link' => $this->getLink(TRUE), - 'dashboard_link' => $dashboard, - 'num_executable_lines' => $this->getNumExecutableLines(), - 'num_executed_lines' => $this->getNumExecutedLines(), - 'lines_executed_percent' => $this->getLineExecutedPercent(), - 'date' => date( - 'D M j G:i:s T Y', - $_SERVER['REQUEST_TIME'] - ), - 'version' => '1.0.4', - 'php_version' => PHP_VERSION, - 'generator' => $generator - ) - ); - } - - /** - * Returns the classes of this node. - * - * @return array - */ - abstract public function getClasses(); - - /** - * 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 methods. - * - * @return integer - */ - abstract public function getNumMethods(); - - /** - * Returns the number of tested methods. - * - * @return integer - */ - abstract public function getNumTestedMethods(); - - /** - * Renders this node. - * - * @param string $target - * @param string $title - * @param string $charset - * @param integer $lowUpperBound - * @param integer $highLowerBound - * @param string $generator - */ - abstract public function render($target, $title, $charset = 'UTF-8', $lowUpperBound = 35, $highLowerBound = 70, $generator = ''); -} - -require_once 'PHP/CodeCoverage/Report/HTML/Node/Directory.php'; -require_once 'PHP/CodeCoverage/Report/HTML/Node/File.php'; diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/File.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/File.php deleted file mode 100644 index b1ea11d..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/File.php +++ /dev/null @@ -1,916 +0,0 @@ -. - * 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-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -if (!defined('T_NAMESPACE')) { - define('T_NAMESPACE', 377); -} - -require_once 'PHP/Token/Stream/CachingFactory.php'; - -/** - * Represents a file in the code coverage information tree. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -class PHP_CodeCoverage_Report_HTML_Node_File extends PHP_CodeCoverage_Report_HTML_Node -{ - /** - * @var array - */ - protected $codeLines; - - /** - * @var array - */ - protected $codeLinesFillup = array(); - - /** - * @var array - */ - protected $executedLines; - - /** - * @var boolean - */ - protected $yui = TRUE; - - /** - * @var boolean - */ - protected $highlight = FALSE; - - /** - * @var integer - */ - protected $numExecutableLines = 0; - - /** - * @var integer - */ - protected $numExecutedLines = 0; - - /** - * @var array - */ - protected $classes = array(); - - /** - * @var integer - */ - protected $numTestedClasses = 0; - - /** - * @var integer - */ - protected $numClasses = NULL; - - /** - * @var integer - */ - protected $numMethods = NULL; - - /** - * @var integer - */ - protected $numTestedMethods = NULL; - - /** - * @var string - */ - protected $yuiPanelJS = ''; - - /** - * @var array - */ - protected $startLines = array(); - - /** - * @var array - */ - protected $endLines = array(); - - /** - * Constructor. - * - * @param string $name - * @param PHP_CodeCoverage_Report_HTML_Node $parent - * @param array $executedLines - * @param boolean $yui - * @param boolean $highlight - * @throws RuntimeException - */ - public function __construct($name, PHP_CodeCoverage_Report_HTML_Node $parent, array $executedLines, $yui = TRUE, $highlight = FALSE) - { - parent::__construct($name, $parent); - - $path = $this->getPath(); - - if (!file_exists($path)) { - throw new RuntimeException( - sprintf('Path "%s" does not exist.', $path) - ); - } - - $this->executedLines = $executedLines; - $this->highlight = $highlight; - $this->yui = $yui; - $this->codeLines = $this->loadFile($path); - $this->ignoredLines = PHP_CodeCoverage_Util::getLinesToBeIgnored( - $path - ); - - $this->calculateStatistics(); - } - - /** - * Returns the classes of this node. - * - * @return array - */ - public function getClasses() - { - return $this->classes; - } - - /** - * 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() - { - if ($this->numClasses === NULL) { - $this->numClasses = count($this->classes); - - if (isset($this->classes['*'])) { - $this->numClasses--; - } - } - - return $this->numClasses; - } - - /** - * Returns the number of tested classes. - * - * @return integer - */ - public function getNumTestedClasses() - { - return $this->numTestedClasses; - } - - /** - * 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++; - } - } - } - } - - 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++; - } - } - } - } - - return $this->numTestedMethods; - } - - /** - * Renders this node. - * - * @param string $target - * @param string $title - * @param string $charset - * @param integer $lowUpperBound - * @param integer $highLowerBound - * @param string $generator - */ - public function render($target, $title, $charset = 'UTF-8', $lowUpperBound = 35, $highLowerBound = 70, $generator = '') - { - if ($this->yui) { - $template = new Text_Template( - PHP_CodeCoverage_Report_HTML::$templatePath . 'file.html' - ); - - $yuiTemplate = new Text_Template( - PHP_CodeCoverage_Report_HTML::$templatePath . 'yui_item.js' - ); - } else { - $template = new Text_Template( - PHP_CodeCoverage_Report_HTML::$templatePath . 'file_no_yui.html' - ); - } - - $i = 1; - $lines = ''; - - foreach ($this->codeLines as $line) { - $css = ''; - - if (!isset($this->ignoredLines[$i]) && - isset($this->executedLines[$i])) { - $count = ''; - - // Array: Line is executable and was executed. - // count(Array) = Number of tests that hit this line. - if (is_array($this->executedLines[$i])) { - $color = 'lineCov'; - $numTests = count($this->executedLines[$i]); - $count = sprintf('%8d', $numTests); - - if ($this->yui) { - $buffer = ''; - $testCSS = ''; - - foreach ($this->executedLines[$i] as $test) { - switch ($test['status']) { - case 0: { - $testCSS = ' class=\"testPassed\"'; - } - break; - - case 1: - case 2: { - $testCSS = ' class=\"testIncomplete\"'; - } - break; - - case 3: { - $testCSS = ' class=\"testFailure\"'; - } - break; - - case 4: { - $testCSS = ' class=\"testError\"'; - } - break; - - default: { - $testCSS = ''; - } - } - - $buffer .= sprintf( - '%s', - - $testCSS, - addslashes(htmlspecialchars($test['id'])) - ); - } - - if ($numTests > 1) { - $header = $numTests . ' tests cover'; - } else { - $header = '1 test covers'; - } - - $header .= ' line ' . $i; - - $yuiTemplate->setVar( - array( - 'line' => $i, - 'header' => $header, - 'tests' => $buffer - ), - FALSE - ); - - $this->yuiPanelJS .= $yuiTemplate->render(); - } - } - - // -1: Line is executable and was not executed. - else if ($this->executedLines[$i] == -1) { - $color = 'lineNoCov'; - $count = sprintf('%8d', 0); - } - - // -2: Line is dead code. - else { - $color = 'lineDeadCode'; - $count = ' '; - } - - $css = sprintf( - ' %s : ', - - $color, - $count - ); - } - - $fillup = array_shift($this->codeLinesFillup); - - if ($fillup > 0) { - $line .= str_repeat(' ', $fillup); - } - - $lines .= sprintf( - ''. - '%8d %s%s%s' . "\n", - - $i, - $i, - $i, - $i, - $i, - !empty($css) ? $css : ' : ', - !$this->highlight ? htmlspecialchars($line) : $line, - !empty($css) ? '' : '' - ); - - $i++; - } - - $items = ''; - - foreach ($this->classes as $className => $classData) { - if ($classData['executedLines'] == $classData['executableLines']) { - $numTestedClasses = 1; - $testedClassesPercent = 100; - } else { - $numTestedClasses = 0; - $testedClassesPercent = 0; - } - - $numMethods = 0; - $numTestedMethods = 0; - - foreach ($classData['methods'] as $method) { - if ($method['executableLines'] > 0) { - $numMethods++; - - if ($method['executedLines'] == $method['executableLines']) { - $numTestedMethods++; - } - } - } - - $items .= $this->doRenderItem( - array( - 'name' => sprintf( - '%s', - - $classData['startLine'], - $className - ), - 'numClasses' => 1, - 'numTestedClasses' => $numTestedClasses, - 'testedClassesPercent' => sprintf( - '%01.2f', $testedClassesPercent - ), - 'numMethods' => $numMethods, - 'numTestedMethods' => $numTestedMethods, - 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent( - $numTestedMethods, $numMethods, TRUE - ), - 'numExecutableLines' => $classData['executableLines'], - 'numExecutedLines' => $classData['executedLines'], - 'executedLinesPercent' => PHP_CodeCoverage_Util::percent( - $classData['executedLines'], - $classData['executableLines'], - TRUE - ) - ), - $lowUpperBound, - $highLowerBound - ); - - foreach ($classData['methods'] as $methodData) { - if ($methodData['executableLines'] > 0) { - if ($methodData['executedLines'] == $methodData['executableLines']) { - $numTestedMethods = 1; - $testedMethodsPercent = 100; - } else { - $numTestedMethods = 0; - $testedMethodsPercent = 0; - } - - $items .= $this->doRenderItem( - array( - 'name' => sprintf( - ' %s', - - $methodData['startLine'], - htmlspecialchars($methodData['signature']) - ), - 'numClasses' => '', - 'numTestedClasses' => '', - 'testedClassesPercent' => '', - 'numMethods' => 1, - 'numTestedMethods' => $numTestedMethods, - 'testedMethodsPercent' => sprintf( - '%01.2f', $testedMethodsPercent - ), - 'numExecutableLines' => $methodData['executableLines'], - 'numExecutedLines' => $methodData['executedLines'], - 'executedLinesPercent' => PHP_CodeCoverage_Util::percent( - $methodData['executedLines'], - $methodData['executableLines'], - TRUE - ), - 'crap' => PHP_CodeCoverage_Util::crap( - $methodData['ccn'], - PHP_CodeCoverage_Util::percent( - $methodData['executedLines'], - $methodData['executableLines'] - ) - ) - ), - $lowUpperBound, - $highLowerBound, - 'method_item.html' - ); - } - } - } - - $this->setTemplateVars($template, $title, $charset, $generator); - - $template->setVar( - array( - 'lines' => $lines, - 'total_item' => $this->renderTotalItem( - $lowUpperBound, $highLowerBound, FALSE - ), - 'items' => $items, - 'yuiPanelJS' => $this->yuiPanelJS - ) - ); - - $cleanId = PHP_CodeCoverage_Util::getSafeFilename($this->getId()); - $template->renderTo($target . $cleanId . '.html'); - - $this->yuiPanelJS = ''; - $this->executedLines = array(); - } - - /** - * Calculates coverage statistics for the file. - * - */ - protected function calculateStatistics() - { - $this->processClasses(); - $this->processFunctions(); - - $max = count($this->codeLines); - - for ($lineNumber = 1; $lineNumber <= $max; $lineNumber++) { - if (isset($this->startLines[$lineNumber])) { - // Start line of a class. - if (isset($this->startLines[$lineNumber]['methods'])) { - $currentClass = &$this->startLines[$lineNumber]; - } - - // Start line of a method. - else { - $currentMethod = &$this->startLines[$lineNumber]; - } - } - - if (isset($this->executedLines[$lineNumber])) { - // Array: Line is executable and was executed. - if (is_array($this->executedLines[$lineNumber])) { - if (isset($currentClass)) { - $currentClass['executableLines']++; - $currentClass['executedLines']++; - } - - if (isset($currentMethod)) { - $currentMethod['executableLines']++; - $currentMethod['executedLines']++; - } - - $this->numExecutableLines++; - $this->numExecutedLines++; - } - - // -1: Line is executable and was not executed. - else if ($this->executedLines[$lineNumber] == -1) { - if (isset($currentClass)) { - $currentClass['executableLines']++; - } - - if (isset($currentMethod)) { - $currentMethod['executableLines']++; - } - - $this->numExecutableLines++; - - if (isset($this->ignoredLines[$lineNumber])) { - if (isset($currentClass)) { - $currentClass['executedLines']++; - } - - if (isset($currentMethod)) { - $currentMethod['executedLines']++; - } - - $this->numExecutedLines++; - } - } - } - - if (isset($this->endLines[$lineNumber])) { - // End line of a class. - if (isset($this->endLines[$lineNumber]['methods'])) { - unset($currentClass); - } - - // End line of a method. - else { - unset($currentMethod); - } - } - } - - foreach ($this->classes as $className => &$class) { - foreach ($class['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } - - $method['crap'] = PHP_CodeCoverage_Util::crap( - $method['ccn'], $method['coverage'] - ); - - $class['ccn'] += $method['ccn']; - } - - if ($className != '*') { - if ($class['executableLines'] > 0) { - $class['coverage'] = ($class['executedLines'] / - $class['executableLines']) * 100; - } else { - $class['coverage'] = 100; - } - - if ($class['coverage'] == 100) { - $this->numTestedClasses++; - } - - $class['crap'] = PHP_CodeCoverage_Util::crap( - $class['ccn'], $class['coverage'] - ); - } - } - } - - /** - * @param string $file - * @return array - * @author Aidan Lister - */ - 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); - $linesLength = array_map('strlen', $lines); - $width = max($linesLength); - - foreach ($linesLength as $line => $length) { - $this->codeLinesFillup[$line] = $width - $length; - } - - 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_ARRAY_CAST: - case T_AS: - case T_BOOLEAN_AND: - case T_BOOLEAN_OR: - case T_BOOL_CAST: - case T_BREAK: - case T_CASE: - case T_CATCH: - case T_CLASS: - case T_CLONE: - case T_CONCAT_EQUAL: - case T_CONTINUE: - case T_DEFAULT: - case T_DOUBLE_ARROW: - case T_DOUBLE_CAST: - 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_END_HEREDOC: - case T_EXIT: - case T_EXTENDS: - case T_FINAL: - case T_FOREACH: - case T_FUNCTION: - case T_GLOBAL: - case T_IF: - case T_INC: - case T_INCLUDE: - case T_INCLUDE_ONCE: - case T_INSTANCEOF: - case T_INT_CAST: - case T_ISSET: - case T_IS_EQUAL: - case T_IS_IDENTICAL: - case T_IS_NOT_IDENTICAL: - case T_IS_SMALLER_OR_EQUAL: - case T_NAMESPACE: - case T_NEW: - case T_OBJECT_CAST: - case T_OBJECT_OPERATOR: - case T_PAAMAYIM_NEKUDOTAYIM: - case T_PRIVATE: - case T_PROTECTED: - case T_PUBLIC: - case T_REQUIRE: - case T_REQUIRE_ONCE: - case T_RETURN: - case T_SL: - case T_SL_EQUAL: - case T_SR: - case T_SR_EQUAL: - case T_START_HEREDOC: - case T_STATIC: - case T_STRING_CAST: - case T_THROW: - case T_TRY: - case T_UNSET_CAST: - 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; - } - - protected function processClasses() - { - $file = $this->getId() . '.html#'; - $tokens = PHP_Token_Stream_CachingFactory::get($this->getPath()); - $classes = $tokens->getClasses(); - unset($tokens); - - foreach ($classes as $className => $class) { - $this->classes[$className] = array( - 'methods' => array(), - 'startLine' => $class['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'file' => $file . $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( - 'signature' => $method['signature'], - 'startLine' => $method['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $method['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'file' => $file . $method['startLine'] - ); - - $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName]; - $this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName]; - } - } - } - - protected function processFunctions() - { - $tokens = PHP_Token_Stream_CachingFactory::get($this->getPath()); - $functions = $tokens->getFunctions(); - unset($tokens); - - if (count($functions) > 0 && !isset($this->classes['*'])) { - $this->classes['*'] = array( - 'methods' => array(), - 'startLine' => 0, - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0 - ); - } - - foreach ($functions as $functionName => $function) { - $this->classes['*']['methods'][$functionName] = array( - 'signature' => $function['signature'], - 'startLine' => $function['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $function['ccn'] - ); - - $this->startLines[$function['startLine']] = &$this->classes['*']['methods'][$functionName]; - $this->endLines[$function['endLine']] = &$this->classes['*']['methods'][$functionName]; - } - } -} diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer.php new file mode 100644 index 0000000..b0c84bc --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php new file mode 100644 index 0000000..7d7446c --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php new file mode 100644 index 0000000..ed05276 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/File.php b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/File.php new file mode 100644 index 0000000..9150d77 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist new file mode 100644 index 0000000..73a11a1 --- /dev/null +++ b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist @@ -0,0 +1,3 @@ +
    +
    +
    diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap-responsive.min.css new file mode 100644 index 0000000..7b0158d --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css new file mode 100644 index 0000000..31d8b96 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/css/style.css new file mode 100644 index 0000000..7efa811 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist new file mode 100644 index 0000000..a09c93a --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory.html.dist new file mode 100644 index 0000000..871333e --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist new file mode 100644 index 0000000..4a19a06 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist new file mode 100644 index 0000000..f00d85e --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.dist new file mode 100644 index 0000000..7bff4e5 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 zcmZvC1yGz#v+m*$LXcp=A$ZWB0fL7wNbp_U*$~{_gL`my3oP#L!5tQYy99Ta`+g_q zKlj|KJ2f@c)ARJx{q*bbkhN_!|Wn*Vos8{TEhUT@5e;_WJsIMMcG5%>DiS&dv_N`4@J0cnAQ-#>RjZ z00W5t&tJ^l-QC*ST1-p~00u^9XJ=AUl7oW-;2a+x2k__T=grN{+1c4XK0ZL~^z^i$ zp&>vEhr@4fZWb380S18T&!0cQ3IKpHF)?v=b_NIm0Q>vwY7D0baZ)n z31Fa5sELUQARIVaU0nqf0XzT+fB_63aA;@<$l~wse|mcA;^G1TmX?-)e)jkGPfkuA z92@|!<>h5S_4f8QP-JRq>d&7)^Yin8l7K8gED$&_FaV?gY+wLjpoW%~7NDe=nHfMG z5DO3j{R9kv5GbssrUpO)OyvVrlx>u0UKD0i;Dpm5S5dY16(DL5l{ixz|mhJU@&-OWCTb7_%}8-fE(P~+XIRO zJU|wp1|S>|J3KrLcz^+v1f&BDpd>&MAaibR4#5A_4(MucZwG9E1h4@u0P@C8;oo+g zIVj7kfJi{oV~E(NZ*h(@^-(Q(C`Psb3KZ{N;^GB(a8NE*Vwc715!9 zr-H4Ao|T_c6+VT_JH9H+P3>iXSt!a$F`>s`jn`w9GZ_~B!{0soaiV|O_c^R2aWa%}O3jUE)WO=pa zs~_Wz08z|ieY5A%$@FcBF9^!1a}m5ks@7gjn;67N>}S~Hrm`4sM5Hh`q7&5-N{|31 z6x1{ol7BnskoViZ0GqbLa#kW`Z)VCjt1MysKg|rT zi!?s##Ck>8c zpi|>$lGlw#@yMNi&V4`6OBGJ(H&7lqLlcTQ&1zWriG_fL>BnFcr~?;E93{M-xIozQ zO=EHQ#+?<}%@wbWWv23#!V70h9MOuUVaU>3kpTvYfc|LBw?&b*89~Gc9i&8tlT#kF ztpbZoAzkdB+UTy=tx%L3Z4)I{zY(Kb)eg{InobSJmNwPZt$14aS-uc4eKuY8h$dtfyxu^a%zA)>fYI&)@ZXky?^{5>xSC?;w4r&td6vBdi%vHm4=XJH!3yL3?Ep+T5aU_>i;yr_XGq zxZfCzUU@GvnoIk+_Nd`aky>S&H!b*{A%L>?*XPAgWL(Vf(k7qUS}>Zn=U(ZfcOc{B z3*tOHH@t5Ub5D~#N7!Fxx}P2)sy{vE_l(R7$aW&CX>c|&HY+7};vUIietK%}!phrCuh+;C@1usp;XLU<8Gq8P!rEI3ieg#W$!= zQcZr{hp>8sF?k&Yl0?B84OneiQxef-4TEFrq3O~JAZR}yEJHA|Xkqd49tR&8oq{zP zY@>J^HBV*(gJvJZc_0VFN7Sx?H7#75E3#?N8Z!C+_f53YU}pyggxx1?wQi5Yb-_`I`_V*SMx5+*P^b=ec5RON-k1cIlsBLk}(HiaJyab0`CI zo0{=1_LO$~oE2%Tl_}KURuX<`+mQN_sTdM&* zkFf!Xtl^e^gTy6ON=&gTn6)$JHQq2)33R@_!#9?BLNq-Wi{U|rVX7Vny$l6#+SZ@KvQt@VYb%<9JfapI^b9j=wa+Tqb4ei;8c5 z&1>Uz@lVFv6T4Z*YU$r4G`g=91lSeA<=GRZ!*KTWKDPR}NPUW%peCUj`Ix_LDq!8| zMH-V`Pv!a~QkTL||L@cqiTz)*G-0=ytr1KqTuFPan9y4gYD5>PleK`NZB$ev@W%t= zkp)_=lBUTLZJpAtZg;pjI;7r2y|26-N7&a(hX|`1YNM9N8{>8JAuv}hp1v`3JHT-=5lbXpbMq7X~2J5Kl zh7tyU`_AusMFZ{ej9D;Uyy;SQ!4nwgSnngsYBwdS&EO3NS*o04)*juAYl;57c2Ly0(DEZ8IY?zSph-kyxu+D`tt@oU{32J#I{vmy=#0ySPK zA+i(A3yl)qmTz*$dZi#y9FS;$;h%bY+;StNx{_R56Otq+?pGe^T^{5d7Gs&?`_r`8 zD&dzOA|j8@3A&FR5U3*eQNBf<4^4W_iS_()*8b4aaUzfk2 zzIcMWSEjm;EPZPk{j{1>oXd}pXAj!NaRm8{Sjz!D=~q3WJ@vmt6ND_?HI~|wUS1j5 z9!S1MKr7%nxoJ3k`GB^7yV~*{n~O~n6($~x5Bu{7s|JyXbAyKI4+tO(zZYMslK;Zc zzeHGVl{`iP@jfSKq>R;{+djJ9n%$%EL()Uw+sykjNQdflkJZSjqV_QDWivbZS~S{K zkE@T^Jcv)Dfm93!mf$XYnCT--_A$zo9MOkPB6&diM8MwOfV?+ApNv`moV@nqn>&lv zYbN1-M|jc~sG|yLN^1R2=`+1ih3jCshg`iP&mY$GMTcY^W^T`WOCX!{-KHmZ#GiRH zYl{|+KLn5!PCLtBy~9i}`#d^gCDDx$+GQb~uc;V#K3OgbbOG0j5{BRG-si%Bo{@lB zGIt+Ain8^C`!*S0d0OSWVO+Z89}}O8aFTZ>p&k}2gGCV zh#<$gswePFxWGT$4DC^8@84_e*^KT74?7n8!$8cg=sL$OlKr&HMh@Rr5%*Wr!xoOl zo7jItnj-xYgVTX)H1=A2bD(tleEH57#V{xAeW_ezISg5OC zg=k>hOLA^urTH_e6*vSYRqCm$J{xo}-x3@HH;bsHD1Z`Pzvsn}%cvfw%Q(}h`Dgtb z0_J^niUmoCM5$*f)6}}qi(u;cPgxfyeVaaVmOsG<)5`6tzU4wyhF;k|~|x>7-2hXpVBpc5k{L4M`Wbe6Q?tr^*B z`Y*>6*&R#~%JlBIitlZ^qGe3s21~h3U|&k%%jeMM;6!~UH|+0+<5V-_zDqZQN79?n?!Aj!Nj`YMO9?j>uqI9-Tex+nJD z%e0#Yca6(zqGUR|KITa?9x-#C0!JKJHO(+fy@1!B$%ZwJwncQW7vGYv?~!^`#L~Um zOL++>4qmqW`0Chc0T23G8|vO)tK=Z2`gvS4*qpqhIJCEv9i&&$09VO8YOz|oZ+ubd zNXVdLc&p=KsSgtmIPLN69P7xYkYQ1vJ?u1g)T!6Ru`k2wkdj*wDC)VryGu2=yb0?F z>q~~e>KZ0d_#7f3UgV%9MY1}vMgF{B8yfE{HL*pMyhYF)WDZ^^3vS8F zGlOhs%g_~pS3=WQ#494@jAXwOtr^Y|TnQ5zki>qRG)(oPY*f}U_=ip_{qB0!%w7~G zWE!P4p3khyW-JJnE>eECuYfI?^d366Shq!Wm#x&jAo>=HdCllE$>DPO0N;y#4G)D2y#B@5=N=+F%Xo2n{gKcPcK2!hP*^WSXl+ut; zyLvVoY>VL{H%Kd9^i~lsb8j4>$EllrparEOJNT?Ym>vJa$(P^tOG)5aVb_5w^*&M0 zYOJ`I`}9}UoSnYg#E(&yyK(tqr^@n}qU2H2DhkK-`2He% zgXr_4kpXoQHxAO9S`wEdmqGU4j=1JdG!OixdqB4PPP6RXA}>GM zumruUUH|ZG2$bBj)Qluj&uB=dRb)?^qomw?Z$X%#D+Q*O97eHrgVB2*mR$bFBU`*} zIem?dM)i}raTFDn@5^caxE^XFXVhBePmH9fqcTi`TLaXiueH=@06sl}>F%}h9H_e9 z>^O?LxM1EjX}NVppaO@NNQr=AtHcH-BU{yBT_vejJ#J)l^cl69Z7$sk`82Zyw7Wxt z=~J?hZm{f@W}|96FUJfy65Gk8?^{^yjhOahUMCNNpt5DJw}ZKH7b!bGiFY9y6OY&T z_N)?Jj(MuLTN36ZCJ6I5Xy7uVlrb$o*Z%=-)kPo9s?<^Yqz~!Z* z_mP8(unFq65XSi!$@YtieSQ!<7IEOaA9VkKI?lA`*(nURvfKL8cX}-+~uw9|_5)uC2`ZHcaeX7L8aG6Ghleg@F9aG%X$#g6^yP5apnB>YTz&EfS{q z9UVfSyEIczebC)qlVu5cOoMzS_jrC|)rQlAzK7sfiW0`M8mVIohazPE9Jzn*qPt%6 zZL8RELY@L09B83@Be;x5V-IHnn$}{RAT#<2JA%ttlk#^(%u}CGze|1JY5MPhbfnYG zIw%$XfBmA-<_pKLpGKwbRF$#P;@_)ech#>vj25sv25VM$ouo)?BXdRcO{)*OwTw)G zv43W~T6ekBMtUD%5Bm>`^Ltv!w4~65N!Ut5twl!Agrzyq4O2Fi3pUMtCU~>9gt_=h-f% z;1&OuSu?A_sJvIvQ+dZNo3?m1%b1+s&UAx?8sUHEe_sB7zkm4R%6)<@oYB_i5>3Ip zIA+?jVdX|zL{)?TGpx+=Ta>G80}0}Ax+722$XFNJsC1gcH56{8B)*)eU#r~HrC&}` z|EWW92&;6y;3}!L5zXa385@?-D%>dSvyK;?jqU2t_R3wvBW;$!j45uQ7tyEIQva;Db}r&bR3kqNSh)Q_$MJ#Uj3Gj1F;)sO|%6z#@<+ zi{pbYsYS#u`X$Nf($OS+lhw>xgjos1OnF^$-I$u;qhJswhH~p|ab*nO>zBrtb0ndn zxV0uh!LN`&xckTP+JW}gznSpU492)u+`f{9Yr)js`NmfYH#Wdtradc0TnKNz@Su!e zu$9}G_=ku;%4xk}eXl>)KgpuT>_<`Ud(A^a++K&pm3LbN;gI}ku@YVrA%FJBZ5$;m zobR8}OLtW4-i+qPPLS-(7<>M{)rhiPoi@?&vDeVq5%fmZk=mDdRV>Pb-l7pP1y6|J z8I>sF+TypKV=_^NwBU^>4JJq<*14GLfM2*XQzYdlqqjnE)gZsPW^E@mp&ww* zW9i>XL=uwLVZ9pO*8K>t>vdL~Ek_NUL$?LQi5sc#1Q-f6-ywKcIT8Kw?C(_3pbR`e|)%9S-({if|E+hR2W!&qfQ&UiF^I!|M#xhdWsenv^wpKCBiuxXbnp85`{i|;BM?Ba`lqTA zyRm=UWJl&E{8JzYDHFu>*Z10-?#A8D|5jW9Ho0*CAs0fAy~MqbwYuOq9jjt9*nuHI zbDwKvh)5Ir$r!fS5|;?Dt>V+@F*v8=TJJF)TdnC#Mk>+tGDGCw;A~^PC`gUt*<(|i zB{{g{`uFehu`$fm4)&k7`u{xIV)yvA(%5SxX9MS80p2EKnLtCZ>tlX>*Z6nd&6-Mv$5rHD*db;&IBK3KH&M<+ArlGXDRdX1VVO4)&R$f4NxXI>GBh zSv|h>5GDAI(4E`@F?EnW zS>#c&Gw6~_XL`qQG4bK`W*>hek4LX*efn6|_MY+rXkNyAuu?NxS%L7~9tD3cn7&p( zCtfqe6sjB&Q-Vs7BP5+%;#Gk};4xtwU!KY0XXbmkUy$kR9)!~?*v)qw00!+Yg^#H> zc#8*z6zZo>+(bud?K<*!QO4ehiTCK&PD4G&n)Tr9X_3r-we z?fI+}-G~Yn93gI6F{}Dw_SC*FLZ)5(85zp4%uubtD)J)UELLkvGk4#tw&Tussa)mTD$R2&O~{ zCI3>fr-!-b@EGRI%g0L8UU%%u_<;e9439JNV;4KSxd|78v+I+8^rmMf3f40Jb}wEszROD?xBZu>Ll3;sUIoNxDK3|j3*sam2tC@@e$ z^!;+AK>efeBJB%ALsQ{uFui)oDoq()2USi?n=6C3#eetz?wPswc={I<8x=(8lE4EIsUfyGNZ{|KYn1IR|=E==f z(;!A5(-2y^2xRFCSPqzHAZn5RCN_bp22T(KEtjA(rFZ%>a4@STrHZflxKoqe9Z4@^ zM*scx_y73?Q{vt6?~WEl?2q*;@8 z3M*&@%l)SQmXkcUm)d@GT2#JdzhfSAP9|n#C;$E8X|pwD!r#X?0P>0ZisQ~TNqupW z*lUY~+ikD`vQb?@SAWX#r*Y+;=_|oacL$2CL$^(mV}aKO77pg}O+-=T1oLBT5sL2i z42Qth2+0@C`c+*D0*5!qy26sis<9a7>LN2{z%Qj49t z=L@x`4$ALHb*3COHoT?5S_c(Hs}g!V>W^=6Q0}zaubkDn)(lTax0+!+%B}9Vqw6{H zvL|BRM`O<@;eVi1DzM!tXtBrA20Ce@^Jz|>%X-t`vi-%WweXCh_LhI#bUg2*pcP~R z*RuTUzBKLXO~~uMd&o$v3@d0shHfUjC6c539PE6rF&;Ufa(Rw@K1*m7?f5)t`MjH0 z)_V(cajV5Am>f!kWcI@5rE8t6$S>5M=k=aRZROH6fA^jJp~2NlR4;Q2>L$7F#RT#9 z>4@1RhWG`Khy>P2j1Yx^BBL{S`niMaxlSWV-JBU0-T9zZ%>7mR3l$~QV$({o0;jTI ze5=cN^!Bc2bT|BcojXp~K#2cM>OTe*cM{Kg-j*CkiW)EGQot^}s;cy8_1_@JA0Whq zlrNr+R;Efa+`6N)s5rH*|E)nYZ3uqkk2C(E7@A|3YI`ozP~9Lexx#*1(r8luq+YPk z{J}c$s` zPM35Fx(YWB3Z5IYnN+L_4|jaR(5iWJi2~l&xy}aU7kW?o-V*6Av2wyZTG!E2KSW2* zGRLQkQU;Oz##ie-Z4fI)WSRxn$(ZcD;TL+;^r=a4(G~H3ZhK$lSXZj?cvyY8%d9JM zzc3#pD^W_QnWy#rx#;c&N@sqHhrnHRmj#i;s%zLm6SE(n&BWpd&f7>XnjV}OlZntI70fq%8~9<7 zMYaw`E-rp49-oC1N_uZTo)Cu%RR2QWdHpzQIcNsoDp`3xfP+`gI?tVQZ4X={qU?(n zV>0ASES^Xuc;9JBji{)RnFL(Lez;8XbB1uWaMp@p?7xhXk6V#!6B@aP4Rz7-K%a>i z?fvf}va_DGUXlI#4--`A3qK7J?-HwnG7O~H2;zR~RLW)_^#La!=}+>KW#anZ{|^D3 B7G?kd literal 0 HcmV?d00001 diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/img/glyphicons-halflings.png b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.js new file mode 100644 index 0000000..0e33fb1 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/js/highcharts.js new file mode 100644 index 0000000..0a7fd0f --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist new file mode 100644 index 0000000..95ac783 --- /dev/null +++ b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.dist @@ -0,0 +1,11 @@ +
    + + + + + + + + + + diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.bar.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.bar.js deleted file mode 100644 index 777ec2e..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.bar.js +++ /dev/null @@ -1,1653 +0,0 @@ - /** - * o------------------------------------------------------------------------------o - * | This file is part of the RGraph package - you can learn more at: | - * | | - * | http://www.rgraph.net | - * | | - * | This package is licensed under the RGraph license. For all kinds of business | - * | purposes there is a small one-time licensing fee to pay and for non | - * | commercial purposes it is free to use. You can read the full license here: | - * | | - * | http://www.rgraph.net/LICENSE.txt | - * o------------------------------------------------------------------------------o - */ - - if (typeof(RGraph) == 'undefined') RGraph = {}; - - /** - * The bar chart constructor - * - * @param object canvas The canvas object - * @param array data The chart data - */ - RGraph.Bar = function (id, data) - { - // Get the canvas and context objects - this.id = id; - this.canvas = document.getElementById(id); - this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; - this.canvas.__object__ = this; - this.type = 'bar'; - this.max = 0; - this.stackedOrGrouped = false; - this.isRGraph = true; - - /** - * Compatibility with older browsers - */ - RGraph.OldBrowserCompat(this.context); - - - // Various config type stuff - this.properties = { - 'chart.background.barcolor1': 'rgba(0,0,0,0)', - 'chart.background.barcolor2': 'rgba(0,0,0,0)', - 'chart.background.grid': true, - 'chart.background.grid.color': '#ddd', - 'chart.background.grid.width': 1, - 'chart.background.grid.hsize': 20, - 'chart.background.grid.vsize': 20, - 'chart.background.grid.vlines': true, - 'chart.background.grid.hlines': true, - 'chart.background.grid.border': true, - 'chart.background.grid.autofit':false, - 'chart.background.grid.autofit.numhlines': 7, - 'chart.background.grid.autofit.numvlines': 20, - 'chart.ytickgap': 20, - 'chart.smallyticks': 3, - 'chart.largeyticks': 5, - 'chart.numyticks': 10, - 'chart.hmargin': 5, - 'chart.strokecolor': '#666', - 'chart.axis.color': 'black', - 'chart.gutter': 25, - 'chart.labels': null, - 'chart.labels.ingraph': null, - 'chart.labels.above': false, - 'chart.labels.above.decimals': 0, - 'chart.labels.above.size': null, - 'chart.ylabels': true, - 'chart.ylabels.count': 5, - 'chart.ylabels.inside': false, - 'chart.xlabels.offset': 0, - 'chart.xaxispos': 'bottom', - 'chart.yaxispos': 'left', - 'chart.text.color': 'black', - 'chart.text.size': 10, - 'chart.text.angle': 0, - 'chart.text.font': 'Verdana', - 'chart.ymax': null, - 'chart.title': '', - 'chart.title.background': null, - 'chart.title.hpos': null, - 'chart.title.vpos': null, - 'chart.title.xaxis': '', - 'chart.title.yaxis': '', - 'chart.title.xaxis.pos': 0.25, - 'chart.title.yaxis.pos': 0.25, - 'chart.colors': ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'], - 'chart.grouping': 'grouped', - 'chart.variant': 'bar', - 'chart.shadow': false, - 'chart.shadow.color': '#666', - 'chart.shadow.offsetx': 3, - 'chart.shadow.offsety': 3, - 'chart.shadow.blur': 3, - 'chart.tooltips': null, - 'chart.tooltips.effect': 'fade', - 'chart.tooltips.css.class': 'RGraph_tooltip', - 'chart.tooltips.event': 'onclick', - 'chart.tooltips.coords.adjust': [0,0], - 'chart.tooltips.highlight': true, - 'chart.background.hbars': null, - - 'chart.key': [], - 'chart.key.background': 'white', - 'chart.key.position': 'graph', - 'chart.key.shadow': false, - 'chart.key.shadow.color': '#666', - 'chart.key.shadow.blur': 3, - 'chart.key.shadow.offsetx': 2, - 'chart.key.shadow.offsety': 2, - 'chart.key.position.gutter.boxed': true, - 'chart.key.position.x': null, - 'chart.key.position.y': null, - 'chart.key.color.shape': 'square', - 'chart.key.rounded': true, - 'chart.key.text.size': 10, - - 'chart.contextmenu': null, - 'chart.line': null, - 'chart.units.pre': '', - 'chart.units.post': '', - 'chart.scale.decimals': 0, - 'chart.scale.point': '.', - 'chart.scale.thousand': ',', - 'chart.crosshairs': false, - 'chart.crosshairs.color': '#333', - 'chart.linewidth': 1, - 'chart.annotatable': false, - 'chart.annotate.color': 'black', - 'chart.zoom.factor': 1.5, - 'chart.zoom.fade.in': true, - 'chart.zoom.fade.out': true, - 'chart.zoom.hdir': 'right', - 'chart.zoom.vdir': 'down', - 'chart.zoom.frames': 10, - 'chart.zoom.delay': 50, - 'chart.zoom.shadow': true, - 'chart.zoom.mode': 'canvas', - 'chart.zoom.thumbnail.width': 75, - 'chart.zoom.thumbnail.height': 75, - 'chart.zoom.background': true, - 'chart.resizable': false, - 'chart.adjustable': false - } - - // Check for support - if (!this.canvas) { - alert('[BAR] No canvas support'); - return; - } - - // Check the common library has been included - if (typeof(RGraph) == 'undefined') { - alert('[BAR] Fatal error: The common library does not appear to have been included'); - } - - /** - * Determine whether the chart will contain stacked or grouped bars - */ - for (i=0; i 0) { - - alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts'); - } - - /** - * Stop the coords array from growing uncontrollably - */ - this.coords = []; - - /** - * Work out a few things. They need to be here because they depend on things you can change before you - * call Draw() but after you instantiate the object - */ - this.max = 0; - this.grapharea = this.canvas.height - ( (2 * this.gutter)); - this.halfgrapharea = this.grapharea / 2; - this.halfTextHeight = this.Get('chart.text.size') / 2; - - // Progressively Draw the chart - RGraph.background.Draw(this); - - - //If it's a sketch chart variant, draw the axes first - if (this.Get('chart.variant') == 'sketch') { - this.DrawAxes(); - this.Drawbars(); - } else { - this.Drawbars(); - this.DrawAxes(); - } - - this.DrawLabels(); - - - // Draw the key if necessary - if (this.Get('chart.key').length) { - RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); - } - - - /** - * Setup the context menu if required - */ - if (this.Get('chart.contextmenu')) { - RGraph.ShowContext(this); - } - - - /** - * Is a line is defined, draw it - */ - var line = this.Get('chart.line'); - - if (line) { - - // Check the length of the data(s) - if (line.original_data[0].length != this.data.length) { - alert("[BAR] You're adding a line with a differing amount of data points to the bar chart - this is not permitted"); - } - - // Check the X axis positions - if (this.Get('chart.xaxispos') != line.Get('chart.xaxispos')) { - alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised"); - } - - line.Set('chart.gutter', this.Get('chart.gutter')); - line.Set('chart.noaxes', true); - line.Set('chart.background.barcolor1', 'rgba(0,0,0,0)'); - line.Set('chart.background.barcolor2', 'rgba(0,0,0,0)'); - line.Set('chart.background.grid', false); - line.Set('chart.ylabels', false); - line.Set('chart.hmargin', (this.canvas.width - (2 * this.gutter)) / (line.original_data[0].length * 2)); - - // If a custom yMax is set, use that - if (this.Get('chart.ymax')) { - line.Set('chart.ymax', this.Get('chart.ymax')); - } - - line.Draw(); - } - - - /** - * Draw "in graph" labels - */ - if (this.Get('chart.labels.ingraph')) { - RGraph.DrawInGraphLabels(this); - } - - /** - * Draw crosschairs - */ - if (this.Get('chart.crosshairs')) { - RGraph.DrawCrosshairs(this); - } - - /** - * If the canvas is annotatable, do install the event handlers - */ - if (this.Get('chart.annotatable')) { - RGraph.Annotate(this); - } - - /** - * This bit shows the mini zoom window if requested - */ - if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { - RGraph.ShowZoomWindow(this); - } - - - /** - * This function enables resizing - */ - if (this.Get('chart.resizable')) { - RGraph.AllowResizing(this); - } - - - /** - * This function enables adjusting - */ - if (this.Get('chart.adjustable')) { - RGraph.AllowAdjusting(this); - } - - /** - * Fire the RGraph ondraw event - */ - RGraph.FireCustomEvent(this, 'ondraw'); - } - - - /** - * Draws the charts axes - */ - RGraph.Bar.prototype.DrawAxes = function () - { - var gutter = this.gutter; - var xaxispos = this.Get('chart.xaxispos'); - var yaxispos = this.Get('chart.yaxispos'); - - this.context.beginPath(); - this.context.strokeStyle = this.Get('chart.axis.color'); - this.context.lineWidth = 1; - - // Draw the Y axis - if (yaxispos == 'right') { - this.context.moveTo(this.canvas.width - gutter, gutter); - this.context.lineTo(this.canvas.width - gutter, this.canvas.height - gutter); - } else { - this.context.moveTo(gutter, gutter); - this.context.lineTo(gutter, this.canvas.height - gutter); - } - - // Draw the X axis - this.context.moveTo(gutter, (xaxispos == 'center' ? this.canvas.height / 2 : this.canvas.height - gutter)); - this.context.lineTo(this.canvas.width - gutter, xaxispos == 'center' ? this.canvas.height / 2 : this.canvas.height - gutter); - - var numYTicks = this.Get('chart.numyticks'); - - // Draw the Y tickmarks - var yTickGap = (this.canvas.height - (2 * gutter)) / numYTicks; - var xpos = yaxispos == 'left' ? gutter : this.canvas.width - gutter; - - for (y=gutter; - xaxispos == 'center' ? y <= (this.canvas.height - gutter) : y < (this.canvas.height - gutter); - y += yTickGap) { - - if (xaxispos == 'center' && y == (this.canvas.height / 2)) continue; - - this.context.moveTo(xpos, y); - this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), y); - } - - // Draw the X tickmarks - xTickGap = (this.canvas.width - (2 * gutter) ) / this.data.length; - yStart = this.canvas.height - gutter; - yEnd = (this.canvas.height - gutter) + 3; - - //////////////// X TICKS //////////////// - - // Now move the Y start end positions down if the axis is set to center - if (xaxispos == 'center') { - yStart = (this.canvas.height / 2) + 3; - yEnd = (this.canvas.height / 2) - 3; - } - - for (x=gutter + (yaxispos == 'left' ? xTickGap : 0); x 0) { - RGraph.DrawBars(this); - } - - var variant = this.Get('chart.variant'); - - /** - * Draw the 3D axes is necessary - */ - if (variant == '3d') { - RGraph.Draw3DAxes(this); - } - - /** - * Get the variant once, and draw the bars, be they regular, stacked or grouped - */ - - // Get these variables outside of the loop - var xaxispos = this.Get('chart.xaxispos'); - var width = (this.canvas.width - (2 * gutter) ) / this.data.length; - var orig_height = height; - var hmargin = this.Get('chart.hmargin'); - var shadow = this.Get('chart.shadow'); - var shadowColor = this.Get('chart.shadow.color'); - var shadowBlur = this.Get('chart.shadow.blur'); - var shadowOffsetX = this.Get('chart.shadow.offsetx'); - var shadowOffsetY = this.Get('chart.shadow.offsety'); - var strokeStyle = this.Get('chart.strokecolor'); - var colors = this.Get('chart.colors'); - - for (i=0; i 0.4 ? -1 : 3) - (r * width),y - 1); - this.context.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2)); - } - - this.context.stroke(); - - // Regular bar - } else if (variant == 'bar' || variant == '3d' || variant == 'glass') { - - if (document.all && shadow) { - this.DrawIEShadow([x + hmargin, y, barWidth, height]); - } - - if (variant == 'glass') { - RGraph.filledCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0); - RGraph.strokedCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0); - } else { - this.context.strokeRect(x + hmargin, y, barWidth, height); - this.context.fillRect(x + hmargin, y, barWidth, height); - } - - - // This bit draws the text labels that appear above the bars if requested - if (this.Get('chart.labels.above')) { - - // Turn off any shadow - if (shadow) { - RGraph.NoShadow(this); - } - - var yPos = y - 3; - - // Account for negative bars - if (this.data[i] < 0) { - yPos += height + 6 + (this.Get('chart.text.size') - 4); - } - - this.context.fillStyle = this.Get('chart.text.color'); - RGraph.Text(this.context, this.Get('chart.text.font'), typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3, x + hmargin + (barWidth / 2), yPos, RGraph.number_format(this, Number(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')),this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); - } - - // 3D effect - if (variant == '3d') { - - var prevStrokeStyle = this.context.strokeStyle; - var prevFillStyle = this.context.fillStyle; - - // Draw the top - this.context.beginPath(); - this.context.moveTo(x + hmargin, y); - this.context.lineTo(x + hmargin + 10, y - 5); - this.context.lineTo(x + hmargin + 10 + barWidth, y - 5); - this.context.lineTo(x + hmargin + barWidth, y); - this.context.closePath(); - - this.context.stroke(); - this.context.fill(); - - // Draw the right hand side - this.context.beginPath(); - this.context.moveTo(x + hmargin + barWidth, y); - this.context.lineTo(x + hmargin + barWidth + 10, y - 5); - this.context.lineTo(x + hmargin + barWidth + 10, y + height - 5); - this.context.lineTo(x + hmargin + barWidth, y + height); - this.context.closePath(); - - this.context.stroke(); - this.context.fill(); - - // Draw the darker top section - this.context.beginPath(); - this.context.fillStyle = 'rgba(255,255,255,0.3)'; - this.context.moveTo(x + hmargin, y); - this.context.lineTo(x + hmargin + 10, y - 5); - this.context.lineTo(x + hmargin + 10 + barWidth, y - 5); - this.context.lineTo(x + hmargin + barWidth, y); - this.context.lineTo(x + hmargin, y); - this.context.closePath(); - - this.context.stroke(); - this.context.fill(); - - // Draw the darker right side section - this.context.beginPath(); - this.context.fillStyle = 'rgba(0,0,0,0.4)'; - this.context.moveTo(x + hmargin + barWidth, y); - this.context.lineTo(x + hmargin + barWidth + 10, y - 5); - this.context.lineTo(x + hmargin + barWidth + 10, y - 5 + height); - this.context.lineTo(x + hmargin + barWidth, y + height); - this.context.lineTo(x + hmargin + barWidth, y); - this.context.closePath(); - - this.context.stroke(); - this.context.fill(); - - this.context.strokeStyle = prevStrokeStyle; - this.context.fillStyle = prevFillStyle; - - // Glass variant - } else if (variant == 'glass') { - - var grad = this.context.createLinearGradient( - x + hmargin, - y, - x + hmargin + (barWidth / 2), - y - ); - grad.addColorStop(0, 'rgba(255,255,255,0.9)'); - grad.addColorStop(1, 'rgba(255,255,255,0.5)'); - - this.context.beginPath(); - this.context.fillStyle = grad; - this.context.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2); - this.context.fill(); - } - - // Dot chart - } else if (variant == 'dot') { - - this.context.beginPath(); - this.context.moveTo(x + (width / 2), y); - this.context.lineTo(x + (width / 2), y + height); - this.context.stroke(); - - this.context.beginPath(); - this.context.fillStyle = this.Get('chart.colors')[i]; - this.context.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0); - - // Set the colour for the dots - this.context.fillStyle = this.Get('chart.colors')[0]; - - this.context.stroke(); - this.context.fill(); - - // Pyramid chart - } else if (variant == 'pyramid') { - - this.context.beginPath(); - var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.Get('chart.gutter'))); - - this.context.moveTo(x + hmargin, startY); - this.context.lineTo( - x + hmargin + (barWidth / 2), - y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0) - ); - this.context.lineTo(x + hmargin + barWidth, startY); - - this.context.closePath(); - - this.context.stroke(); - this.context.fill(); - - // Arrow chart - } else if (variant == 'arrow') { - var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.gutter)); - - this.context.lineWidth = this.Get('chart.linewidth') ? this.Get('chart.linewidth') : 1; - this.context.lineCap = 'round'; - - this.context.beginPath(); - - this.context.moveTo(x + hmargin + (barWidth / 2), startY); - this.context.lineTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)); - this.context.arc(x + hmargin + (barWidth / 2), - y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0), - 5, - this.data[i] > 0 ? 0.78 : 5.6, - this.data[i] > 0 ? 0.79 : 5.48, - this.data[i] < 0); - - this.context.moveTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)); - this.context.arc(x + hmargin + (barWidth / 2), - y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0), - 5, - this.data[i] > 0 ? 2.355 : 4, - this.data[i] > 0 ? 2.4 : 3.925, - this.data[i] < 0); - - this.context.stroke(); - - this.context.lineWidth = 1; - - // Unknown variant type - } else { - alert('[BAR] Warning! Unknown chart.variant: ' + variant); - } - - this.coords.push([x + hmargin, y, width - (2 * hmargin), height]); - - - /** - * Stacked bar - */ - } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') { - - var barWidth = width - (2 * hmargin); - var redrawCoords = [];// Necessary to draw if the shadow is enabled - var startY = 0; - - for (j=0; j 0) { - - /** - * Get the tooltip text - */ - if (typeof(obj.Get('chart.tooltips')) == 'function') { - var text = String(obj.Get('chart.tooltips')(barCoords[5])); - - } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'function') { - var text = String(obj.Get('chart.tooltips')[barCoords[5]](barCoords[5])); - - } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'string' || typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'number')) { - var text = String(obj.Get('chart.tooltips')[barCoords[5]]); - - } else { - var text = null; - } - - if (text) { - canvas.style.cursor = 'pointer'; - } else { - canvas.style.cursor = 'default'; - } - - /** - * Hide the currently displayed tooltip if the index is the same - */ - if ( RGraph.Registry.Get('chart.tooltip') - && RGraph.Registry.Get('chart.tooltip').__canvas__.id != obj.id - && obj.Get('chart.tooltips.event') == 'onmousemove') { - - RGraph.Redraw(); - RGraph.HideTooltip(); - } - - /** - * This facilitates the tooltips using the onmousemove event - */ - - if ( obj.Get('chart.tooltips.event') == 'onmousemove' - && ( - (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ != barCoords[5]) - || !RGraph.Registry.Get('chart.tooltip') - ) - && text) { - /** - * Show a tooltip if it's defined - */ - RGraph.Redraw(obj); - - obj.context.beginPath(); - obj.context.strokeStyle = 'black'; - obj.context.fillStyle = 'rgba(255,255,255,0.5)'; - obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); - obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); - - obj.context.stroke(); - obj.context.fill(); - - RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]); - } - } else { - canvas.style.cursor = 'default'; - } - } - RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove); - this.canvas.addEventListener('mousemove', canvas_onmousemove, false); - - - /** - * Install the onclick event handler for the tooltips - */ - if (this.Get('chart.tooltips.event') == 'onclick') { - - canvas_onclick = function (e) - { - var e = RGraph.FixEventObject(e); - - // If the button pressed isn't the left, we're not interested - if (e.button != 0) return; - - e = RGraph.FixEventObject(e); - - var canvas = document.getElementById(this.id); - var obj = canvas.__object__; - var barCoords = obj.getBar(e); - - /** - * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn - * This "deselects" any already selected bar - */ - RGraph.Redraw(); - - /** - * Loop through the bars determining if the mouse is over a bar - */ - if (barCoords) { - - /** - * Get the tooltip text - */ - if (typeof(obj.Get('chart.tooltips')) == 'function') { - var text = String(obj.Get('chart.tooltips')(barCoords[5])); - - } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'function') { - var text = String(obj.Get('chart.tooltips')[barCoords[5]](barCoords[5])); - - } else if (typeof(obj.Get('chart.tooltips')) == 'object') { - var text = String(obj.Get('chart.tooltips')[barCoords[5]]); - - } else { - var text = null; - } - - /** - * Show a tooltip if it's defined - */ - if (text && text != 'undefined') { - - // [TODO] Allow customisation of the highlight colors - obj.context.beginPath(); - obj.context.strokeStyle = 'black'; - obj.context.fillStyle = 'rgba(255,255,255,0.5)'; - obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); - obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); - - obj.context.stroke(); - obj.context.fill(); - - RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]); - } - } - - /** - * Stop the event bubbling - */ - e.stopPropagation(); - } - RGraph.AddEventListener(this.id, 'click', canvas_onclick); - this.canvas.addEventListener('click', canvas_onclick, false); - } - - - // This resets the bar graph - // 8th August 2010 : Is this redundant - //if (typeof(obj) != 'undefined' && obj == RGraph.Registry.Get('chart.tooltip')) { - // obj.style.display = 'none'; - // RGraph.Registry.Set('chart.tooltip', null) - //} - } - } - - /** - * Draws the labels for the graph - */ - RGraph.Bar.prototype.DrawLabels = function () - { - var context = this.context; - var gutter = this.gutter; - var text_angle = this.Get('chart.text.angle'); - var text_size = this.Get('chart.text.size'); - var labels = this.Get('chart.labels'); - - - // Draw the Y axis labels: - if (this.Get('chart.ylabels')) { - this.Drawlabels_center(); - this.Drawlabels_bottom(); - } - - /** - * The X axis labels - */ - if (typeof(labels) == 'object' && labels) { - - var yOffset = 13 + Number(this.Get('chart.xlabels.offset')); - - /** - * Text angle - */ - var angle = 0; - var halign = 'center'; - - if (text_angle > 0) { - angle = -1 * text_angle; - halign = 'right'; - yOffset -= 5; - } - - // Draw the X axis labels - context.fillStyle = this.Get('chart.text.color'); - - // How wide is each bar - var barWidth = (this.canvas.width - (2 * gutter) ) / labels.length; - - // Reset the xTickGap - xTickGap = (this.canvas.width - (2 * gutter)) / labels.length - - // Draw the X tickmarks - var i=0; - var font = this.Get('chart.text.font'); - - for (x=gutter + (xTickGap / 2); x<=this.canvas.width - gutter; x+=xTickGap) { - RGraph.Text(context, font, - text_size, - x + (this.Get('chart.text.angle') == 90 ? 0: 0), - (this.canvas.height - gutter) + yOffset, - String(labels[i++]), - (this.Get('chart.text.angle') == 90 ? 'center' : null), - halign, - null, - angle); - } - } - } - - /** - * Draws the X axis in the middle - */ - RGraph.Bar.prototype.Drawlabels_center = function () - { - var font = this.Get('chart.text.font'); - var numYLabels = this.Get('chart.ylabels.count'); - - this.context.fillStyle = this.Get('chart.text.color'); - - if (this.Get('chart.xaxispos') == 'center') { - - /** - * Draw the top labels - */ - var interval = (this.grapharea * (1/10) ); - var text_size = this.Get('chart.text.size'); - var gutter = this.gutter; - var units_pre = this.Get('chart.units.pre'); - var units_post = this.Get('chart.units.post'); - var context = this.context; - var align = ''; - var xpos = 0; - var boxed = false; - - this.context.fillStyle = this.Get('chart.text.color'); - this.context.strokeStyle = 'black'; - - if (this.Get('chart.ylabels.inside') == true) { - var xpos = this.Get('chart.yaxispos') == 'left' ? gutter + 5 : this.canvas.width - gutter - 5; - var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right'; - var boxed = true; - } else { - var xpos = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : this.canvas.width - gutter + 5; - var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left'; - var boxed = false; - } - - - - - - - - - - - - - /** - * Draw specific Y labels here so that the local variables can be reused - */ - if (typeof(this.Get('chart.ylabels.specific')) == 'object') { - - var labels = this.Get('chart.ylabels.specific'); - var grapharea = this.canvas.height - (2 * gutter); - - // Draw the top halves labels - for (var i=0; i=0; --i) { - var y = gutter + (grapharea * ( (i+1) / (labels.length * 2) )) + (grapharea / 2); - - RGraph.Text(context, font, text_size, xpos, y, labels[labels.length - i - 1], 'center', align, boxed); - } - - return; - } - - - - - - - - - - - - - if (numYLabels == 3 || numYLabels == 5) { - RGraph.Text(context, font, text_size, xpos, gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); - - if (numYLabels == 5) { - RGraph.Text(context, font, text_size, xpos, (1*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); - RGraph.Text(context, font, text_size, xpos, (3*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); - } - - if (numYLabels == 3 || numYLabels == 5) { - RGraph.Text(context, font, text_size, xpos, (4*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); - RGraph.Text(context, font, text_size, xpos, (2*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); - } - } else if (numYLabels == 10) { - // 10Y labels - interval = (this.grapharea / numYLabels) / 2; - - for (var i=0; i= (left + obj.Get('chart.tooltips.coords.adjust')[0]) - && mouseX <= (left + width+ obj.Get('chart.tooltips.coords.adjust')[0]) - && mouseY >= (top + obj.Get('chart.tooltips.coords.adjust')[1]) - && mouseY <= (top + height + obj.Get('chart.tooltips.coords.adjust')[1]) ) { - - return [obj, left, top, width, height, i]; - } - } - - return null; - } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.common.core.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.common.core.js deleted file mode 100644 index b3b26b7..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.common.core.js +++ /dev/null @@ -1,2454 +0,0 @@ - /** - * o------------------------------------------------------------------------------o - * | This file is part of the RGraph package - you can learn more at: | - * | | - * | http://www.rgraph.net | - * | | - * | This package is licensed under the RGraph license. For all kinds of business | - * | purposes there is a small one-time licensing fee to pay and for non | - * | commercial purposes it is free to use. You can read the full license here: | - * | | - * | http://www.rgraph.net/LICENSE.txt | - * o------------------------------------------------------------------------------o - */ - - /** - * Initialise the various objects - */ - if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; - - - RGraph.Registry = {}; - RGraph.Registry.store = []; - RGraph.Registry.store['chart.event.handlers'] = []; - RGraph.background = {}; - RGraph.objects = []; - RGraph.Resizing = {}; - RGraph.events = []; - - - - /** - * Returns five values which are used as a nice scale - * - * @param max int The maximum value of the graph - * @param obj object The graph object - * @return array An appropriate scale - */ - RGraph.getScale = function (max, obj) - { - /** - * Special case for 0 - */ - if (max == 0) { - return ['0.2', '0.4', '0.6', '0.8', '1.0']; - } - - var original_max = max; - - /** - * Manually do decimals - */ - if (max <= 1) { - if (max > 0.5) { - return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)]; - - } else if (max >= 0.1) { - return obj.Get('chart.scale.round') ? [0.2,0.4,0.6,0.8,1] : [0.1,0.2,0.3,0.4,0.5]; - - } else { - - var tmp = max; - var exp = 0; - - while (tmp < 1.01) { - exp += 1; - tmp *= 10; - } - - var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp]; - - - if (max <= ('5e-' + exp)) { - ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp]; - } - - return ret; - } - } - - // Take off any decimals - if (String(max).indexOf('.') > 0) { - max = String(max).replace(/\.\d+$/, ''); - } - - var interval = Math.pow(10, Number(String(Number(max)).length - 1)); - var topValue = interval; - - while (topValue < max) { - topValue += (interval / 2); - } - - // Handles cases where the max is (for example) 50.5 - if (Number(original_max) > Number(topValue)) { - topValue += (interval / 2); - } - - // Custom if the max is greater than 5 and less than 10 - if (max < 10) { - topValue = (Number(original_max) <= 5 ? 5 : 10); - } - - /** - * Added 02/11/2010 to create "nicer" scales - */ - if (obj && typeof(obj.Get('chart.scale.round')) == 'boolean' && obj.Get('chart.scale.round')) { - topValue = 10 * interval; - } - - return [topValue * 0.2, topValue * 0.4, topValue * 0.6, topValue * 0.8, topValue]; - } - - - /** - * Returns the maximum value which is in an array - * - * @param array arr The array - * @param int Whether to ignore signs (ie negative/positive) - * @return int The maximum value in the array - */ - RGraph.array_max = function (arr) - { - var max = null; - - for (var i=0; i ' + RGraph.pr(obj[i], true, indent + ' ') + '\n'; - } - - var str = str + indent + ')'; - break; - - case 'function': - str += obj; - break; - - case 'boolean': - str += 'Boolean: ' + (obj ? 'true' : 'false'); - break; - } - - /** - * Finished, now either return if we're in a recursed call, or alert() - * if we're not. - */ - if (arguments[1]) { - return str; - } else { - alert(str); - } - } - - - /** - * The RGraph registry Set() function - * - * @param string name The name of the key - * @param mixed value The value to set - * @return mixed Returns the same value as you pass it - */ - RGraph.Registry.Set = function (name, value) - { - // Store the setting - RGraph.Registry.store[name] = value; - - // Don't really need to do this, but ho-hum - return value; - } - - - /** - * The RGraph registry Get() function - * - * @param string name The name of the particular setting to fetch - * @return mixed The value if exists, null otherwise - */ - RGraph.Registry.Get = function (name) - { - //return RGraph.Registry.store[name] == null ? null : RGraph.Registry.store[name]; - return RGraph.Registry.store[name]; - } - - - /** - * This function draws the background for the bar chart, line chart and scatter chart. - * - * @param object obj The graph object - */ - RGraph.background.Draw = function (obj) - { - var canvas = obj.canvas; - var context = obj.context; - var height = 0; - var gutter = obj.Get('chart.gutter'); - var variant = obj.Get('chart.variant'); - - context.fillStyle = obj.Get('chart.text.color'); - - // If it's a bar and 3D variant, translate - if (variant == '3d') { - context.save(); - context.translate(10, -5); - } - - // X axis title - if (typeof(obj.Get('chart.title.xaxis')) == 'string' && obj.Get('chart.title.xaxis').length) { - - var size = obj.Get('chart.text.size'); - var font = obj.Get('chart.text.font'); - - context.beginPath(); - RGraph.Text(context, font, size + 2, obj.canvas.width / 2, canvas.height - (gutter * obj.Get('chart.title.xaxis.pos')), obj.Get('chart.title.xaxis'), 'center', 'center', false, false, false, true); - context.fill(); - } - - // Y axis title - if (typeof(obj.Get('chart.title.yaxis')) == 'string' && obj.Get('chart.title.yaxis').length) { - - var size = obj.Get('chart.text.size'); - var font = obj.Get('chart.text.font'); - - context.beginPath(); - RGraph.Text(context, font, size + 2, gutter * obj.Get('chart.title.yaxis.pos'), canvas.height / 2, obj.Get('chart.title.yaxis'), 'center', 'center', false, 270, false, true); - context.fill(); - } - - obj.context.beginPath(); - - // Draw the horizontal bars - context.fillStyle = obj.Get('chart.background.barcolor1'); - height = (obj.canvas.height - obj.Get('chart.gutter')); - - for (var i=gutter; i < height ; i+=80) { - obj.context.fillRect(gutter, i, obj.canvas.width - (gutter * 2), Math.min(40, obj.canvas.height - gutter - i) ); - } - - context.fillStyle = obj.Get('chart.background.barcolor2'); - height = (obj.canvas.height - gutter); - - for (var i= (40 + gutter); i < height; i+=80) { - obj.context.fillRect(gutter, i, obj.canvas.width - (gutter * 2), i + 40 > (obj.canvas.height - gutter) ? obj.canvas.height - (gutter + i) : 40); - } - - context.stroke(); - - - // Draw the background grid - if (obj.Get('chart.background.grid')) { - - // If autofit is specified, use the .numhlines and .numvlines along with the width to work - // out the hsize and vsize - if (obj.Get('chart.background.grid.autofit')) { - var vsize = (canvas.width - (2 * obj.Get('chart.gutter')) - (obj.type == 'gantt' ? 2 * obj.Get('chart.gutter') : 0)) / obj.Get('chart.background.grid.autofit.numvlines'); - var hsize = (canvas.height - (2 * obj.Get('chart.gutter'))) / obj.Get('chart.background.grid.autofit.numhlines'); - - obj.Set('chart.background.grid.vsize', vsize); - obj.Set('chart.background.grid.hsize', hsize); - } - - context.beginPath(); - context.lineWidth = obj.Get('chart.background.grid.width') ? obj.Get('chart.background.grid.width') : 1; - context.strokeStyle = obj.Get('chart.background.grid.color'); - - // Draw the horizontal lines - if (obj.Get('chart.background.grid.hlines')) { - height = (canvas.height - gutter) - for (y=gutter; y < height; y+=obj.Get('chart.background.grid.hsize')) { - context.moveTo(gutter, y); - context.lineTo(canvas.width - gutter, y); - } - } - - if (obj.Get('chart.background.grid.vlines')) { - // Draw the vertical lines - var width = (canvas.width - gutter) - for (x=gutter + (obj.type == 'gantt' ? (2 * gutter) : 0); x<=width; x+=obj.Get('chart.background.grid.vsize')) { - context.moveTo(x, gutter); - context.lineTo(x, obj.canvas.height - gutter); - } - } - - if (obj.Get('chart.background.grid.border')) { - // Make sure a rectangle, the same colour as the grid goes around the graph - context.strokeStyle = obj.Get('chart.background.grid.color'); - context.strokeRect(gutter, gutter, canvas.width - (2 * gutter), canvas.height - (2 * gutter)); - } - } - - context.stroke(); - - // If it's a bar and 3D variant, translate - if (variant == '3d') { - context.restore(); - } - - // Draw the title if one is set - if ( typeof(obj.Get('chart.title')) == 'string') { - - if (obj.type == 'gantt') { - gutter /= 2; - } - - RGraph.DrawTitle(canvas, obj.Get('chart.title'), gutter, null, obj.Get('chart.text.size') + 2); - } - - context.stroke(); - } - - - /** - * Returns the day number for a particular date. Eg 1st February would be 32 - * - * @param object obj A date object - * @return int The day number of the given date - */ - RGraph.GetDays = function (obj) - { - var year = obj.getFullYear(); - var days = obj.getDate(); - var month = obj.getMonth(); - - if (month == 0) return days; - if (month >= 1) days += 31; - if (month >= 2) days += 28; - - // Leap years. Crude, but if this code is still being used - // when it stops working, then you have my permission to shoot - // me. Oh, you won't be able to - I'll be dead... - if (year >= 2008 && year % 4 == 0) days += 1; - - if (month >= 3) days += 31; - if (month >= 4) days += 30; - if (month >= 5) days += 31; - if (month >= 6) days += 30; - if (month >= 7) days += 31; - if (month >= 8) days += 31; - if (month >= 9) days += 30; - if (month >= 10) days += 31; - if (month >= 11) days += 30; - - return days; - } - - - - - - - - - - - - - - - - /** - * Draws the graph key (used by various graphs) - * - * @param object obj The graph object - * @param array key An array of the texts to be listed in the key - * @param colors An array of the colors to be used - */ - RGraph.DrawKey = function (obj, key, colors) - { - var canvas = obj.canvas; - var context = obj.context; - context.lineWidth = 1; - - context.beginPath(); - - /** - * Key positioned in the gutter - */ - var keypos = obj.Get('chart.key.position'); - var textsize = obj.Get('chart.text.size'); - var gutter = obj.Get('chart.gutter'); - - /** - * Change the older chart.key.vpos to chart.key.position.y - */ - if (typeof(obj.Get('chart.key.vpos')) == 'number') { - obj.Set('chart.key.position.y', obj.Get('chart.key.vpos') * gutter); - } - - if (keypos && keypos == 'gutter') { - - RGraph.DrawKey_gutter(obj, key, colors); - - - /** - * In-graph style key - */ - } else if (keypos && keypos == 'graph') { - - RGraph.DrawKey_graph(obj, key, colors); - - } else { - alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos); - } - } - - - - - - /** - * This does the actual drawing of the key when it's in the graph - * - * @param object obj The graph object - * @param array key The key items to draw - * @param array colors An aray of colors that the key will use - */ - RGraph.DrawKey_graph = function (obj, key, colors) - { - var canvas = obj.canvas; - var context = obj.context; - var text_size = typeof(obj.Get('chart.key.text.size')) == 'number' ? obj.Get('chart.key.text.size') : obj.Get('chart.text.size'); - var text_font = obj.Get('chart.text.font'); - var gutter = obj.Get('chart.gutter'); - var hpos = obj.Get('chart.yaxispos') == 'right' ? gutter + 10 : canvas.width - gutter - 10; - var vpos = gutter + 10; - var title = obj.Get('chart.title'); - var blob_size = text_size; // The blob of color - var hmargin = 8; // This is the size of the gaps between the blob of color and the text - var vmargin = 4; // This is the vertical margin of the key - var fillstyle = obj.Get('chart.key.background'); - var strokestyle = 'black'; - var height = 0; - var width = 0; - - - // Need to set this so that measuring the text works out OK - context.font = text_size + 'pt ' + obj.Get('chart.text.font'); - - // Work out the longest bit of text - for (i=0; i=0; i--) { - var j = Number(i) + 1; - - // Draw the blob of color - if (obj.Get('chart.key.color.shape') == 'circle') { - context.beginPath(); - context.strokeStyle = 'rgba(0,0,0,0)'; - context.fillStyle = colors[i]; - context.arc(hpos + 5 + (blob_size / 2), vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2), blob_size / 2, 0, 6.26, 0); - context.fill(); - - } else if (obj.Get('chart.key.color.shape') == 'line') { - context.beginPath(); - context.strokeStyle = colors[i]; - context.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2)); - context.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2)); - context.stroke(); - - } else { - context.fillStyle = colors[i]; - context.fillRect(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size, text_size, text_size + 1); - } - - context.beginPath(); - - context.fillStyle = 'black'; - - RGraph.Text(context, - text_font, - text_size, - hpos + blob_size + 5 + 5, - vpos + (5 * j) + (text_size * j), - key[i]); - } - context.fill(); - } - - - - - - - /** - * This does the actual drawing of the key when it's in the gutter - * - * @param object obj The graph object - * @param array key The key items to draw - * @param array colors An aray of colors that the key will use - */ - RGraph.DrawKey_gutter = function (obj, key, colors) - { - var canvas = obj.canvas; - var context = obj.context; - var text_size = typeof(obj.Get('chart.key.text.size')) == 'number' ? obj.Get('chart.key.text.size') : obj.Get('chart.text.size'); - var text_font = obj.Get('chart.text.font'); - var gutter = obj.Get('chart.gutter'); - var hpos = canvas.width / 2; - var vpos = (gutter / 2) - 5; - var title = obj.Get('chart.title'); - var blob_size = text_size; // The blob of color - var hmargin = 8; // This is the size of the gaps between the blob of color and the text - var vmargin = 4; // This is the vertical margin of the key - var fillstyle = obj.Get('chart.key.background'); - var strokestyle = 'black'; - var length = 0; - - - - // Need to work out the length of the key first - context.font = text_size + 'pt ' + text_font; - for (i=0; i=0; i--) { - newarr.push(arr[i]); - } - - return newarr; - } - - - /** - * Formats a number with thousand seperators so it's easier to read - * - * @param integer num The number to format - * @param string The (optional) string to prepend to the string - * @param string The (optional) string to ap - * pend to the string - * @return string The formatted number - */ - RGraph.number_format = function (obj, num) - { - var i; - var prepend = arguments[2] ? String(arguments[2]) : ''; - var append = arguments[3] ? String(arguments[3]) : ''; - var output = ''; - var decimal = ''; - var decimal_seperator = obj.Get('chart.scale.point') ? obj.Get('chart.scale.point') : '.'; - var thousand_seperator = obj.Get('chart.scale.thousand') ? obj.Get('chart.scale.thousand') : ','; - RegExp.$1 = ''; - var i,j; - - // Ignore the preformatted version of "1e-2" - if (String(num).indexOf('e') > 0) { - return String(prepend + String(num) + append); - } - - // We need then number as a string - num = String(num); - - // Take off the decimal part - we re-append it later - if (num.indexOf('.') > 0) { - num = num.replace(/\.(.*)/, ''); - decimal = RegExp.$1; - } - - // Thousand seperator - //var seperator = arguments[1] ? String(arguments[1]) : ','; - var seperator = thousand_seperator; - - /** - * Work backwards adding the thousand seperators - */ - var foundPoint; - for (i=(num.length - 1),j=0; i>=0; j++,i--) { - var character = num.charAt(i); - - if ( j % 3 == 0 && j != 0) { - output += seperator; - } - - /** - * Build the output - */ - output += character; - } - - /** - * Now need to reverse the string - */ - var rev = output; - output = ''; - for (i=(rev.length - 1); i>=0; i--) { - output += rev.charAt(i); - } - - // Tidy up - output = output.replace(/^-,/, '-'); - - // Reappend the decimal - if (decimal.length) { - output = output + decimal_seperator + decimal; - decimal = ''; - RegExp.$1 = ''; - } - - // Minor bugette - if (output.charAt(0) == '-') { - output *= -1; - prepend = '-' + prepend; - } - - return prepend + output + append; - } - - - /** - * Draws horizontal coloured bars on something like the bar, line or scatter - */ - RGraph.DrawBars = function (obj) - { - var hbars = obj.Get('chart.background.hbars'); - - /** - * Draws a horizontal bar - */ - obj.context.beginPath(); - - for (i=0; i obj.max) { - hbars[i][1] = obj.max - hbars[i][0]; - } - - - // If height is negative, and the abs() value is greater than .max, use a negative max instead - if (Math.abs(hbars[i][1]) > obj.max) { - hbars[i][1] = -1 * obj.max; - } - - - // If start point is greater than max, change it to max - if (Math.abs(hbars[i][0]) > obj.max) { - hbars[i][0] = obj.max; - } - - // If start point plus height is less than negative max, use the negative max plus the start point - if (hbars[i][0] + hbars[i][1] < (-1 * obj.max) ) { - hbars[i][1] = -1 * (obj.max + hbars[i][0]); - } - - // If the X axis is at the bottom, and a negative max is given, warn the user - if (obj.Get('chart.xaxispos') == 'bottom' && (hbars[i][0] < 0 || (hbars[i][1] + hbars[i][1] < 0)) ) { - alert('[' + obj.type.toUpperCase() + ' (ID: ' + obj.id + ') BACKGROUND HBARS] You have a negative value in one of your background hbars values, whilst the X axis is in the center'); - } - - var ystart = (obj.grapharea - ((hbars[i][0] / obj.max) * obj.grapharea)); - var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / obj.max) * obj.grapharea; - - // Account for the X axis being in the center - if (obj.Get('chart.xaxispos') == 'center') { - ystart /= 2; - height /= 2; - } - - ystart += obj.Get('chart.gutter') - - var x = obj.Get('chart.gutter'); - var y = ystart - height; - var w = obj.canvas.width - (2 * obj.Get('chart.gutter')); - var h = height; - - // Accommodate Opera :-/ - if (navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0) { - h *= -1; - y = y - h; - } - - obj.context.fillStyle = hbars[i][2]; - obj.context.fillRect(x, y, w, h); - } - - obj.context.fill(); - } - - - /** - * Draws in-graph labels. - * - * @param object obj The graph object - */ - RGraph.DrawInGraphLabels = function (obj) - { - var canvas = obj.canvas; - var context = obj.context; - var labels = obj.Get('chart.labels.ingraph'); - var labels_processed = []; - - // Defaults - var fgcolor = 'black'; - var bgcolor = 'white'; - var direction = 1; - - if (!labels) { - return; - } - - /** - * Preprocess the labels array. Numbers are expanded - */ - for (var i=0; i 0) { - - for (var i=0; i 0) { - var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]); - var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]); - var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25; - - context.beginPath(); - context.fillStyle = 'black'; - context.strokeStyle = 'black'; - - - if (obj.type == 'bar') { - - if (obj.Get('chart.variant') == 'dot') { - context.moveTo(x, obj.coords[i][1] - 5); - context.lineTo(x, obj.coords[i][1] - 5 - length); - - var text_x = x; - var text_y = obj.coords[i][1] - 5 - length; - - } else if (obj.Get('chart.variant') == 'arrow') { - context.moveTo(x, obj.coords[i][1] - 5); - context.lineTo(x, obj.coords[i][1] - 5 - length); - - var text_x = x; - var text_y = obj.coords[i][1] - 5 - length; - - } else { - - context.arc(x, y, 2.5, 0, 6.28, 0); - context.moveTo(x, y); - context.lineTo(x, y - length); - - var text_x = x; - var text_y = y - length; - } - - context.stroke(); - context.fill(); - - - } else if (obj.type == 'line') { - - if ( - typeof(labels_processed[i]) == 'object' && - typeof(labels_processed[i][3]) == 'number' && - labels_processed[i][3] == -1 - ) { - - context.moveTo(x, y + 5); - context.lineTo(x, y + 5 + length); - - context.stroke(); - context.beginPath(); - - // This draws the arrow - context.moveTo(x, y + 5); - context.lineTo(x - 3, y + 10); - context.lineTo(x + 3, y + 10); - context.closePath(); - - var text_x = x; - var text_y = y + 5 + length; - - } else { - - var text_x = x; - var text_y = y - 5 - length; - - context.moveTo(x, y - 5); - context.lineTo(x, y - 5 - length); - - context.stroke(); - context.beginPath(); - - // This draws the arrow - context.moveTo(x, y - 5); - context.lineTo(x - 3, y - 10); - context.lineTo(x + 3, y - 10); - context.closePath(); - } - - context.fill(); - } - - - // Taken out on the 10th Nov 2010 - unnecessary - //var width = context.measureText(labels[i]).width; - - context.beginPath(); - - // Fore ground color - context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black'; - - RGraph.Text(context, - obj.Get('chart.text.font'), - obj.Get('chart.text.size'), - text_x, - text_y, - (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i], - 'bottom', - 'center', - true, - null, - (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white'); - context.fill(); - } - } - } - } - } - - - /** - * This function "fills in" key missing properties that various implementations lack - * - * @param object e The event object - */ - RGraph.FixEventObject = function (e) - { - if (RGraph.isIE8()) { - - var e = event; - - e.pageX = (event.clientX + document.body.scrollLeft); - e.pageY = (event.clientY + document.body.scrollTop); - e.target = event.srcElement; - - if (!document.body.scrollTop && document.documentElement.scrollTop) { - e.pageX += parseInt(document.documentElement.scrollLeft); - e.pageY += parseInt(document.documentElement.scrollTop); - } - } - - // This is mainly for FF which doesn't provide offsetX - if (typeof(e.offsetX) == 'undefined' && typeof(e.offsetY) == 'undefined') { - var coords = RGraph.getMouseXY(e); - e.offsetX = coords[0]; - e.offsetY = coords[1]; - } - - // Any browser that doesn't implement stopPropagation() (MSIE) - if (!e.stopPropagation) { - e.stopPropagation = function () {window.event.cancelBubble = true;} - } - - return e; - } - - - /** - * Draw crosshairs if enabled - * - * @param object obj The graph object (from which we can get the context and canvas as required) - */ - RGraph.DrawCrosshairs = function (obj) - { - if (obj.Get('chart.crosshairs')) { - var canvas = obj.canvas; - var context = obj.context; - - // 5th November 2010 - removed now that tooltips are DOM2 based. - //if (obj.Get('chart.tooltips') && obj.Get('chart.tooltips').length > 0) { - //alert('[' + obj.type.toUpperCase() + '] Sorry - you cannot have crosshairs enabled with tooltips! Turning off crosshairs...'); - //obj.Set('chart.crosshairs', false); - //return; - //} - - canvas.onmousemove = function (e) - { - var e = RGraph.FixEventObject(e); - var canvas = obj.canvas; - var context = obj.context; - var gutter = obj.Get('chart.gutter'); - var width = canvas.width; - var height = canvas.height; - var adjustments = obj.Get('chart.tooltips.coords.adjust'); - - var mouseCoords = RGraph.getMouseXY(e); - var x = mouseCoords[0]; - var y = mouseCoords[1]; - - if (typeof(adjustments) == 'object' && adjustments[0] && adjustments[1]) { - x = x - adjustments[0]; - y = y - adjustments[1]; - } - - RGraph.Clear(canvas); - obj.Draw(); - - if ( x >= gutter - && y >= gutter - && x <= (width - gutter) - && y <= (height - gutter) - ) { - - var linewidth = obj.Get('chart.crosshairs.linewidth'); - context.lineWidth = linewidth ? linewidth : 1; - - context.beginPath(); - context.strokeStyle = obj.Get('chart.crosshairs.color'); - - // Draw a top vertical line - context.moveTo(x, gutter); - context.lineTo(x, height - gutter); - - // Draw a horizontal line - context.moveTo(gutter, y); - context.lineTo(width - gutter, y); - - context.stroke(); - - /** - * Need to show the coords? - */ - if (obj.Get('chart.crosshairs.coords')) { - if (obj.type == 'scatter') { - - var xCoord = (((x - obj.Get('chart.gutter')) / (obj.canvas.width - (2 * obj.Get('chart.gutter')))) * (obj.Get('chart.xmax') - obj.Get('chart.xmin'))) + obj.Get('chart.xmin'); - xCoord = xCoord.toFixed(obj.Get('chart.scale.decimals')); - var yCoord = obj.max - (((y - obj.Get('chart.gutter')) / (obj.canvas.height - (2 * obj.Get('chart.gutter')))) * obj.max); - - if (obj.type == 'scatter' && obj.Get('chart.xaxispos') == 'center') { - yCoord = (yCoord - (obj.max / 2)) * 2; - } - - yCoord = yCoord.toFixed(obj.Get('chart.scale.decimals')); - var div = RGraph.Registry.Get('chart.coordinates.coords.div'); - var mouseCoords = RGraph.getMouseXY(e); - var canvasXY = RGraph.getCanvasXY(canvas); - - if (!div) { - - div = document.createElement('DIV'); - div.__object__ = obj; - div.style.position = 'absolute'; - div.style.backgroundColor = 'white'; - div.style.border = '1px solid black'; - div.style.fontFamily = 'Arial, Verdana, sans-serif'; - div.style.fontSize = '10pt' - div.style.padding = '2px'; - div.style.opacity = 1; - div.style.WebkitBorderRadius = '3px'; - div.style.borderRadius = '3px'; - div.style.MozBorderRadius = '3px'; - document.body.appendChild(div); - - RGraph.Registry.Set('chart.coordinates.coords.div', div); - } - - // Convert the X/Y pixel coords to correspond to the scale - - div.style.opacity = 1; - div.style.display = 'inline'; - - if (!obj.Get('chart.crosshairs.coords.fixed')) { - div.style.left = Math.max(2, (e.pageX - div.offsetWidth - 3)) + 'px'; - div.style.top = Math.max(2, (e.pageY - div.offsetHeight - 3)) + 'px'; - } else { - div.style.left = canvasXY[0] + obj.Get('chart.gutter') + 3 + 'px'; - div.style.top = canvasXY[1] + obj.Get('chart.gutter') + 3 + 'px'; - } - - div.innerHTML = '' + obj.Get('chart.crosshairs.coords.labels.x') + ': ' + xCoord + '
    ' + obj.Get('chart.crosshairs.coords.labels.y') + ': ' + yCoord; - - canvas.addEventListener('mouseout', RGraph.HideCrosshairCoords, false); - - } else { - alert('[RGRAPH] Showing crosshair coordinates is only supported on the Scatter chart'); - } - } - } else { - RGraph.HideCrosshairCoords(); - } - } - } - } - - /** - * Thisz function hides the crosshairs coordinates - */ - RGraph.HideCrosshairCoords = function () - { - var div = RGraph.Registry.Get('chart.coordinates.coords.div'); - - if ( div - && div.style.opacity == 1 - && div.__object__.Get('chart.crosshairs.coords.fadeout') - ) { - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.9;}, 50); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.8;}, 100); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.7;}, 150); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.6;}, 200); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.5;}, 250); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.4;}, 300); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.3;}, 350); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.2;}, 400); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.1;}, 450); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0;}, 500); - setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.display = 'none';}, 550); - } - } - - - /** - * Trims the right hand side of a string. Removes SPACE, TAB - * CR and LF. - * - * @param string str The string to trim - */ - RGraph.rtrim = function (str) - { - return str.replace(/( |\n|\r|\t)+$/, ''); - } - - - /** - * Draws the3D axes/background - */ - RGraph.Draw3DAxes = function (obj) - { - var gutter = obj.Get('chart.gutter'); - var context = obj.context; - var canvas = obj.canvas; - - context.strokeStyle = '#aaa'; - context.fillStyle = '#ddd'; - - // Draw the vertical left side - context.beginPath(); - context.moveTo(gutter, gutter); - context.lineTo(gutter + 10, gutter - 5); - context.lineTo(gutter + 10, canvas.height - gutter - 5); - context.lineTo(gutter, canvas.height - gutter); - context.closePath(); - - context.stroke(); - context.fill(); - - // Draw the bottom floor - context.beginPath(); - context.moveTo(gutter, canvas.height - gutter); - context.lineTo(gutter + 10, canvas.height - gutter - 5); - context.lineTo(canvas.width - gutter + 10, canvas.height - gutter - 5); - context.lineTo(canvas.width - gutter, canvas.height - gutter); - context.closePath(); - - context.stroke(); - context.fill(); - } - - /** - * Turns off any shadow - * - * @param object obj The graph object - */ - RGraph.NoShadow = function (obj) - { - obj.context.shadowColor = 'rgba(0,0,0,0)'; - obj.context.shadowBlur = 0; - obj.context.shadowOffsetX = 0; - obj.context.shadowOffsetY = 0; - } - - - /** - * Sets the four shadow properties - a shortcut function - * - * @param object obj Your graph object - * @param string color The shadow color - * @param number offsetx The shadows X offset - * @param number offsety The shadows Y offset - * @param number blur The blurring effect applied to the shadow - */ - RGraph.SetShadow = function (obj, color, offsetx, offsety, blur) - { - obj.context.shadowColor = color; - obj.context.shadowOffsetX = offsetx; - obj.context.shadowOffsetY = offsety; - obj.context.shadowBlur = blur; - } - - - /** - * This function attempts to "fill in" missing functions from the canvas - * context object. Only two at the moment - measureText() nd fillText(). - * - * @param object context The canvas 2D context - */ - RGraph.OldBrowserCompat = function (context) - { - if (!context.measureText) { - - // This emulates the measureText() function - context.measureText = function (text) - { - var textObj = document.createElement('DIV'); - textObj.innerHTML = text; - textObj.style.backgroundColor = 'white'; - textObj.style.position = 'absolute'; - textObj.style.top = -100 - textObj.style.left = 0; - document.body.appendChild(textObj); - - var width = {width: textObj.offsetWidth}; - - textObj.style.display = 'none'; - - return width; - } - } - - if (!context.fillText) { - // This emulates the fillText() method - context.fillText = function (text, targetX, targetY) - { - return false; - } - } - - // If IE8, add addEventListener() - if (!context.canvas.addEventListener) { - window.addEventListener = function (ev, func, bubble) - { - return this.attachEvent('on' + ev, func); - } - - context.canvas.addEventListener = function (ev, func, bubble) - { - return this.attachEvent('on' + ev, func); - } - } - } - - - /** - * This function is for use with circular graph types, eg the Pie or Radar. Pass it your event object - * and it will pass you back the corresponding segment details as an array: - * - * [x, y, r, startAngle, endAngle] - * - * Angles are measured in degrees, and are measured from the "east" axis (just like the canvas). - * - * @param object e Your event object - */ - RGraph.getSegment = function (e) - { - RGraph.FixEventObject(e); - - // The optional arg provides a way of allowing some accuracy (pixels) - var accuracy = arguments[1] ? arguments[1] : 0; - - var obj = e.target.__object__; - var canvas = obj.canvas; - var context = obj.context; - var mouseCoords = RGraph.getMouseXY(e); - var x = mouseCoords[0] - obj.centerx; - var y = mouseCoords[1] - obj.centery; - var r = obj.radius; - var theta = Math.atan(y / x); // RADIANS - var hyp = y / Math.sin(theta); - var angles = obj.angles; - var ret = []; - var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy; - - - // Put theta in DEGREES - theta *= 57.3 - - // hyp should not be greater than radius if it's a Rose chart - if (obj.type == 'rose') { - if ( (isNaN(hyp) && Math.abs(mouseCoords[0]) < (obj.centerx - r) ) - || (isNaN(hyp) && Math.abs(mouseCoords[0]) > (obj.centerx + r)) - || (!isNaN(hyp) && Math.abs(hyp) > r)) { - return; - } - } - - /** - * Account for the correct quadrant - */ - if (x < 0 && y >= 0) { - theta += 180; - } else if (x < 0 && y < 0) { - theta += 180; - } else if (x > 0 && y < 0) { - theta += 360; - } - - /** - * Account for the rose chart - */ - if (obj.type == 'rose') { - theta += 90; - } - - if (theta > 360) { - theta -= 360; - } - - for (var i=0; i= angles[i][0] && theta < angles[i][1]) { - - hyp = Math.abs(hyp); - - if (obj.type == 'rose' && hyp > angles[i][2]) { - return null; - } - - if (!hyp || (obj.type == 'pie' && obj.radius && hyp > obj.radius) ) { - return null; - } - - if (obj.type == 'pie' && obj.Get('chart.variant') == 'donut' && (hyp > obj.radius || hyp < (obj.radius / 2) ) ) { - return null; - } - - ret[0] = obj.centerx; - ret[1] = obj.centery; - ret[2] = (obj.type == 'rose') ? angles[i][2] : obj.radius; - ret[3] = angles[i][0]; - ret[4] = angles[i][1]; - ret[5] = i; - - if (obj.type == 'rose') { - - ret[3] -= 90; - ret[4] -= 90; - - if (x > 0 && y < 0) { - ret[3] += 360; - ret[4] += 360; - } - } - - if (ret[3] < 0) ret[3] += 360; - if (ret[4] > 360) ret[4] -= 360; - - return ret; - } - } - - return null; - } - - - /** - * This is a function that can be used to run code asynchronously, which can - * be used to speed up the loading of you pages. - * - * @param string func This is the code to run. It can also be a function pointer. - * The front page graphs show this function in action. Basically - * each graphs code is made in a function, and that function is - * passed to this function to run asychronously. - */ - RGraph.Async = function (func) - { - return setTimeout(func, arguments[1] ? arguments[1] : 1); - } - - - /** - * A custom random number function - * - * @param number min The minimum that the number should be - * @param number max The maximum that the number should be - * @param number How many decimal places there should be. Default for this is 0 - */ - RGraph.random = function (min, max) - { - var dp = arguments[2] ? arguments[2] : 0; - var r = Math.random(); - - return Number((((max - min) * r) + min).toFixed(dp)); - } - - - /** - * Draws a rectangle with curvy corners - * - * @param context object The context - * @param x number The X coordinate (top left of the square) - * @param y number The Y coordinate (top left of the square) - * @param w number The width of the rectangle - * @param h number The height of the rectangle - * @param number The radius of the curved corners - * @param boolean Whether the top left corner is curvy - * @param boolean Whether the top right corner is curvy - * @param boolean Whether the bottom right corner is curvy - * @param boolean Whether the bottom left corner is curvy - */ - RGraph.strokedCurvyRect = function (context, x, y, w, h) - { - // The corner radius - var r = arguments[5] ? arguments[5] : 3; - - // The corners - var corner_tl = (arguments[6] || arguments[6] == null) ? true : false; - var corner_tr = (arguments[7] || arguments[7] == null) ? true : false; - var corner_br = (arguments[8] || arguments[8] == null) ? true : false; - var corner_bl = (arguments[9] || arguments[9] == null) ? true : false; - - context.beginPath(); - - // Top left side - context.moveTo(x + (corner_tl ? r : 0), y); - context.lineTo(x + w - (corner_tr ? r : 0), y); - - // Top right corner - if (corner_tr) { - context.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2, false); - } - - // Top right side - context.lineTo(x + w, y + h - (corner_br ? r : 0) ); - - // Bottom right corner - if (corner_br) { - context.arc(x + w - r, y - r + h, r, Math.PI * 2, Math.PI * 0.5, false); - } - - // Bottom right side - context.lineTo(x + (corner_bl ? r : 0), y + h); - - // Bottom left corner - if (corner_bl) { - context.arc(x + r, y - r + h, r, Math.PI * 0.5, Math.PI, false); - } - - // Bottom left side - context.lineTo(x, y + (corner_tl ? r : 0) ); - - // Top left corner - if (corner_tl) { - context.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5, false); - } - - context.stroke(); - } - - - /** - * Draws a filled rectangle with curvy corners - * - * @param context object The context - * @param x number The X coordinate (top left of the square) - * @param y number The Y coordinate (top left of the square) - * @param w number The width of the rectangle - * @param h number The height of the rectangle - * @param number The radius of the curved corners - * @param boolean Whether the top left corner is curvy - * @param boolean Whether the top right corner is curvy - * @param boolean Whether the bottom right corner is curvy - * @param boolean Whether the bottom left corner is curvy - */ - RGraph.filledCurvyRect = function (context, x, y, w, h) - { - // The corner radius - var r = arguments[5] ? arguments[5] : 3; - - // The corners - var corner_tl = (arguments[6] || arguments[6] == null) ? true : false; - var corner_tr = (arguments[7] || arguments[7] == null) ? true : false; - var corner_br = (arguments[8] || arguments[8] == null) ? true : false; - var corner_bl = (arguments[9] || arguments[9] == null) ? true : false; - - context.beginPath(); - - // First draw the corners - - // Top left corner - if (corner_tl) { - context.moveTo(x + r, y + r); - context.arc(x + r, y + r, r, Math.PI, 1.5 * Math.PI, false); - } else { - context.fillRect(x, y, r, r); - } - - // Top right corner - if (corner_tr) { - context.moveTo(x + w - r, y + r); - context.arc(x + w - r, y + r, r, 1.5 * Math.PI, 0, false); - } else { - context.moveTo(x + w - r, y); - context.fillRect(x + w - r, y, r, r); - } - - - // Bottom right corner - if (corner_br) { - context.moveTo(x + w - r, y + h - r); - context.arc(x + w - r, y - r + h, r, 0, Math.PI / 2, false); - } else { - context.moveTo(x + w - r, y + h - r); - context.fillRect(x + w - r, y + h - r, r, r); - } - - // Bottom left corner - if (corner_bl) { - context.moveTo(x + r, y + h - r); - context.arc(x + r, y - r + h, r, Math.PI / 2, Math.PI, false); - } else { - context.moveTo(x, y + h - r); - context.fillRect(x, y + h - r, r, r); - } - - // Now fill it in - context.fillRect(x + r, y, w - r - r, h); - context.fillRect(x, y + r, r + 1, h - r - r); - context.fillRect(x + w - r - 1, y + r, r + 1, h - r - r); - - context.fill(); - } - - - /** - * A crude timing function - * - * @param string label The label to use for the time - */ - RGraph.Timer = function (label) - { - var d = new Date(); - - // This uses the Firebug console - console.log(label + ': ' + d.getSeconds() + '.' + d.getMilliseconds()); - } - - - /** - * Hides the palette if it's visible - */ - RGraph.HidePalette = function () - { - var div = RGraph.Registry.Get('palette'); - - if (typeof(div) == 'object' && div) { - div.style.visibility = 'hidden'; - div.style.display = 'none'; - RGraph.Registry.Set('palette', null); - } - } - - - /** - * Hides the zoomed canvas - */ - RGraph.HideZoomedCanvas = function () - { - if (typeof(__zoomedimage__) == 'object') { - obj = __zoomedimage__.obj; - } else { - return; - } - - if (obj.Get('chart.zoom.fade.out')) { - for (var i=10,j=1; i>=0; --i, ++j) { - if (typeof(__zoomedimage__) == 'object') { - setTimeout("__zoomedimage__.style.opacity = " + String(i / 10), j * 30); - } - } - - if (typeof(__zoomedbackground__) == 'object') { - setTimeout("__zoomedbackground__.style.opacity = " + String(i / 10), j * 30); - } - } - - if (typeof(__zoomedimage__) == 'object') { - setTimeout("__zoomedimage__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? 310 : 0); - } - - if (typeof(__zoomedbackground__) == 'object') { - setTimeout("__zoomedbackground__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? 310 : 0); - } - } - - - /** - * Adds an event handler - * - * @param object obj The graph object - * @param string event The name of the event, eg ontooltip - * @param object func The callback function - */ - RGraph.AddCustomEventListener = function (obj, name, func) - { - if (typeof(RGraph.events[obj.id]) == 'undefined') { - RGraph.events[obj.id] = []; - } - - RGraph.events[obj.id].push([obj, name, func]); - } - - - /** - * Used to fire one of the RGraph custom events - * - * @param object obj The graph object that fires the event - * @param string event The name of the event to fire - */ - RGraph.FireCustomEvent = function (obj, name) - { - for (i in RGraph.events) { - if (typeof(i) == 'string' && i == obj.id && RGraph.events[i].length > 0) { - for(var j=0; j 0; - } - - - /** - * Checks the browser for traces of MSIE9 - */ - RGraph.isIE9 = function () - { - return navigator.userAgent.indexOf('MSIE 9') > 0; - } - - - /** - * Checks the browser for traces of MSIE9 - */ - RGraph.isIE9up = function () - { - navigator.userAgent.match(/MSIE (\d+)/); - - return Number(RegExp.$1) >= 9; - } - - - /** - * This clears a canvases event handlers. - * - * @param string id The ID of the canvas whose event handlers will be cleared - */ - RGraph.ClearEventListeners = function (id) - { - for (var i=0; i str.length ? obj.Get('chart.labels')[i] : str); - } - } - - obj.context.font = obj.Get('chart.text.size') + 'pt ' + obj.Get('chart.text.font'); - - len = obj.context.measureText(str).width + 5; - - return (obj.type == 'hbar' ? len / 3 : len); - } - - - /** - * A basic Array shift gunction - * - * @param object The numerical array to work on - * @return The new array - */ - RGraph.array_shift = function (arr) - { - var ret = []; - - for (var i=1; i document.body.offsetWidth ? RGraph.Registry.Get('chart.tooltip').offsetWidth : 0); - var diffy = y - currenty - RGraph.Registry.Get('chart.tooltip').offsetHeight; - - // Position the tooltip - setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.2)) + 'px"', 25); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.4)) + 'px"', 50); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.6)) + 'px"', 75); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.8)) + 'px"', 100); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 1.0)) + 'px"', 125); - - setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.2)) + 'px"', 25); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.4)) + 'px"', 50); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.6)) + 'px"', 75); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.8)) + 'px"', 100); - setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 1.0)) + 'px"', 125); - - } else { - - alert('[TOOLTIPS] The "snap" effect is only supported on the Line, Rscatter, Scatter and Tradar charts'); - } - - /** - * Fire the tooltip event - */ - RGraph.FireCustomEvent(canvas.__object__, 'ontooltip'); - - return; - } - - /** - * Hide any currently shown tooltip - */ - RGraph.HideTooltip(); - - - /** - * Show a tool tip - */ - var tooltipObj = document.createElement('DIV'); - tooltipObj.className = canvas.__object__.Get('chart.tooltips.css.class'); - tooltipObj.style.display = 'none'; - tooltipObj.style.position = 'absolute'; - tooltipObj.style.left = 0; - tooltipObj.style.top = 0; - tooltipObj.style.backgroundColor = '#ffe'; - tooltipObj.style.color = 'black'; - if (!document.all) tooltipObj.style.border = '1px solid rgba(0,0,0,0)'; - tooltipObj.style.visibility = 'visible'; - tooltipObj.style.paddingLeft = RGraph.tooltips.padding; - tooltipObj.style.paddingRight = RGraph.tooltips.padding; - tooltipObj.style.fontFamily = RGraph.tooltips.font_face; - tooltipObj.style.fontSize = RGraph.tooltips.font_size; - tooltipObj.style.zIndex = 3; - tooltipObj.style.borderRadius = '5px'; - tooltipObj.style.MozBorderRadius = '5px'; - tooltipObj.style.WebkitBorderRadius = '5px'; - tooltipObj.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; - tooltipObj.style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; - tooltipObj.style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; - tooltipObj.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)'; - tooltipObj.style.opacity = 0; - tooltipObj.style.overflow = 'hidden'; - tooltipObj.innerHTML = text; - tooltipObj.__text__ = text; // This is set because the innerHTML can change when it's set - tooltipObj.__canvas__ = canvas; - tooltipObj.style.display = 'inline'; - - if (typeof(idx) == 'number') { - tooltipObj.__index__ = idx; - } - - document.body.appendChild(tooltipObj); - - var width = tooltipObj.offsetWidth; - var height = tooltipObj.offsetHeight - (RGraph.isIE9up() ? 7 : 0); - - if ((y - height - 2) > 0) { - y = y - height - 2; - } else { - y = y + 2; - } - - /** - * Set the width on the tooltip so it doesn't resize if the window is resized - */ - tooltipObj.style.width = width + 'px'; - //tooltipObj.style.height = 0; // Initially set the tooltip height to nothing - - /** - * If the mouse is towards the right of the browser window and the tooltip would go outside of the window, - * move it left - */ - if ( (x + width) > document.body.offsetWidth ) { - x = x - width - 7; - var placementLeft = true; - - if (canvas.__object__.Get('chart.tooltips.effect') == 'none') { - x = x - 3; - } - - tooltipObj.style.left = x + 'px'; - tooltipObj.style.top = y + 'px'; - - } else { - x += 5; - - tooltipObj.style.left = x + 'px'; - tooltipObj.style.top = y + 'px'; - } - - - if (effect == 'expand') { - - tooltipObj.style.left = (x + (width / 2)) + 'px'; - tooltipObj.style.top = (y + (height / 2)) + 'px'; - leftDelta = (width / 2) / 10; - topDelta = (height / 2) / 10; - - tooltipObj.style.width = 0; - tooltipObj.style.height = 0; - tooltipObj.style.boxShadow = ''; - tooltipObj.style.MozBoxShadow = ''; - tooltipObj.style.WebkitBoxShadow = ''; - tooltipObj.style.borderRadius = 0; - tooltipObj.style.MozBorderRadius = 0; - tooltipObj.style.WebkitBorderRadius = 0; - tooltipObj.style.opacity = 1; - - // Progressively move the tooltip to where it should be (the x position) - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 250)); - - // Progressively move the tooltip to where it should be (the Y position) - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 250)); - - // Progressively grow the tooltip width - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.1) + "px'; }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.2) + "px'; }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.3) + "px'; }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.4) + "px'; }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.5) + "px'; }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.6) + "px'; }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.7) + "px'; }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.8) + "px'; }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.9) + "px'; }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250)); - - // Progressively grow the tooltip height - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.1) + "px'; }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.2) + "px'; }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.3) + "px'; }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.4) + "px'; }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.5) + "px'; }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.6) + "px'; }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.7) + "px'; }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.8) + "px'; }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.9) + "px'; }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250)); - - // When the animation is finished, set the tooltip HTML - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.borderRadius = '5px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.MozBorderRadius = '5px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.WebkitBorderRadius = '5px'; }", 250)); - - } else if (effect == 'contract') { - - tooltipObj.style.left = (x - width) + 'px'; - tooltipObj.style.top = (y - (height * 2)) + 'px'; - tooltipObj.style.cursor = 'pointer'; - - leftDelta = width / 10; - topDelta = height / 10; - - tooltipObj.style.width = (width * 5) + 'px'; - tooltipObj.style.height = (height * 5) + 'px'; - - tooltipObj.style.opacity = 0.2; - - // Progressively move the tooltip to where it should be (the x position) - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 250)); - - // Progressively move the tooltip to where it should be (the Y position) - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 250)); - - // Progressively shrink the tooltip width - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 5.5) + "px'; }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 5.0) + "px'; }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 4.5) + "px'; }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 4.0) + "px'; }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 3.5) + "px'; }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 3.0) + "px'; }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 2.5) + "px'; }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 2.0) + "px'; }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 1.5) + "px'; }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250)); - - // Progressively shrink the tooltip height - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 5.5) + "px'; }", 25)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 5.0) + "px'; }", 50)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 4.5) + "px'; }", 75)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 4.0) + "px'; }", 100)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 3.5) + "px'; }", 125)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 3.0) + "px'; }", 150)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 2.5) + "px'; }", 175)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 2.0) + "px'; }", 200)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 1.5) + "px'; }", 225)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250)); - - // When the animation is finished, set the tooltip HTML - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.borderRadius = '5px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.MozBorderRadius = '5px'; }", 250)); - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.WebkitBorderRadius = '5px'; }", 250)); - - /** - * This resets the pointer - */ - RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.cursor = 'default'; }", 275)); - - - - } else if (effect != 'fade' && effect != 'expand' && effect != 'none' && effect != 'snap' && effect != 'contract') { - alert('[COMMON] Unknown tooltip effect: ' + effect); - } - - if (effect != 'none') { - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.1; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 25); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.2; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 50); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.3; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 75); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.4; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 100); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.5; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 125); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.6; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 150); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.7; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.4)'; }", 175); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.8; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.6)'; }", 200); - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.9; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.8)'; }", 225); - } - - setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 1;RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgb(96,96,96)';}", effect == 'none' ? 50 : 250); - - /** - * If the tooltip it self is clicked, cancel it - */ - tooltipObj.onmousedown = function (e) - { - e = RGraph.FixEventObject(e) - e.stopPropagation(); - } - - tooltipObj.onclick = function (e) - { - if (e.button == 0) { - e = RGraph.FixEventObject(e); - e.stopPropagation(); - } - } - - /** - * Install the function for hiding the tooltip. - */ - document.body.onmousedown = function (event) - { - var tooltip = RGraph.Registry.Get('chart.tooltip'); - - if (tooltip) { - RGraph.HideTooltip(); - - // Redraw if highlighting is enabled - if (tooltip.__canvas__.__object__.Get('chart.tooltips.highlight')) { - RGraph.Redraw(); - } - } - } - - /** - * If the window is resized, hide the tooltip - */ - window.onresize = function () - { - var tooltip = RGraph.Registry.Get('chart.tooltip'); - - if (tooltip) { - tooltip.parentNode.removeChild(tooltip); - tooltip.style.display = 'none'; - tooltip.style.visibility = 'hidden'; - RGraph.Registry.Set('chart.tooltip', null); - - // Redraw the graph if necessary - if (canvas.__object__.Get('chart.tooltips.highlight')) { - RGraph.Clear(canvas); - canvas.__object__.Draw(); - } - } - } - - /** - * Keep a reference to the tooltip - */ - RGraph.Registry.Set('chart.tooltip', tooltipObj); - - /** - * Fire the tooltip event - */ - RGraph.FireCustomEvent(canvas.__object__, 'ontooltip'); - } - - - /** - * - */ - RGraph.getTooltipText = function (text) - { - var result = /^id:(.*)/.exec(text); - - if (result) { - text = document.getElementById(result[1]).innerHTML; - } - - return text; - } - - - /** - * - */ - RGraph.getTooltipWidth = function (text, obj) - { - var div = document.createElement('DIV'); - div.className = obj.Get('chart.tooltips.css.class'); - div.style.paddingLeft = RGraph.tooltips.padding; - div.style.paddingRight = RGraph.tooltips.padding; - div.style.fontFamily = RGraph.tooltips.font_face; - div.style.fontSize = RGraph.tooltips.font_size; - div.style.visibility = 'hidden'; - div.style.position = 'absolute'; - div.style.top = '300px'; - div.style.left = 0; - div.style.display = 'inline'; - div.innerHTML = RGraph.getTooltipText(text); - document.body.appendChild(div); - - return div.offsetWidth; - } - - - /** - * Hides the currently shown tooltip - */ - RGraph.HideTooltip = function () - { - var tooltip = RGraph.Registry.Get('chart.tooltip'); - - if (tooltip) { - tooltip.parentNode.removeChild(tooltip); - tooltip.style.display = 'none'; - tooltip.style.visibility = 'hidden'; - RGraph.Registry.Set('chart.tooltip', null); - } - } \ No newline at end of file diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.scatter.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.scatter.js deleted file mode 100644 index e87202c..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/RGraph.scatter.js +++ /dev/null @@ -1,1168 +0,0 @@ - /** - * o------------------------------------------------------------------------------o - * | This file is part of the RGraph package - you can learn more at: | - * | | - * | http://www.rgraph.net | - * | | - * | This package is licensed under the RGraph license. For all kinds of business | - * | purposes there is a small one-time licensing fee to pay and for non | - * | commercial purposes it is free to use. You can read the full license here: | - * | | - * | http://www.rgraph.net/LICENSE.txt | - * o------------------------------------------------------------------------------o - */ - - if (typeof(RGraph) == 'undefined') RGraph = {}; - - /** - * The scatter graph constructor - * - * @param object canvas The cxanvas object - * @param array data The chart data - */ - RGraph.Scatter = function (id, data) - { - // Get the canvas and context objects - this.id = id; - this.canvas = document.getElementById(id); - this.canvas.__object__ = this; - this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; - this.max = 0; - this.coords = []; - this.data = []; - this.type = 'scatter'; - this.isRGraph = true; - - - /** - * Compatibility with older browsers - */ - RGraph.OldBrowserCompat(this.context); - - - // Various config properties - this.properties = { - 'chart.background.barcolor1': 'white', - 'chart.background.barcolor2': 'white', - 'chart.background.grid': true, - 'chart.background.grid.width': 1, - 'chart.background.grid.color': '#ddd', - 'chart.background.grid.hsize': 20, - 'chart.background.grid.vsize': 20, - 'chart.background.hbars': null, - 'chart.background.vbars': null, - 'chart.background.grid.vlines': true, - 'chart.background.grid.hlines': true, - 'chart.background.grid.border': true, - 'chart.background.grid.autofit':false, - 'chart.background.grid.autofit.numhlines': 7, - 'chart.background.grid.autofit.numvlines': 20, - 'chart.text.size': 10, - 'chart.text.angle': 0, - 'chart.text.color': 'black', - 'chart.text.font': 'Verdana', - 'chart.tooltips.effect': 'fade', - 'chart.tooltips.hotspot': 3, - 'chart.tooltips.css.class': 'RGraph_tooltip', - 'chart.tooltips.highlight': true, - 'chart.tooltips.coords.adjust': [0,0], - 'chart.units.pre': '', - 'chart.units.post': '', - 'chart.tickmarks': 'cross', - 'chart.ticksize': 5, - 'chart.xticks': true, - 'chart.xaxis': true, - 'chart.gutter': 25, - 'chart.xmax': 0, - 'chart.ymax': null, - 'chart.ymin': null, - 'chart.scale.decimals': null, - 'chart.scale.point': '.', - 'chart.scale.thousand': ',', - 'chart.title': '', - 'chart.title.background': null, - 'chart.title.hpos': null, - 'chart.title.vpos': null, - 'chart.title.xaxis': '', - 'chart.title.yaxis': '', - 'chart.title.xaxis.pos': 0.25, - 'chart.title.yaxis.pos': 0.25, - 'chart.labels': [], - 'chart.ylabels': true, - 'chart.ylabels.count': 5, - 'chart.contextmenu': null, - 'chart.defaultcolor': 'black', - 'chart.xaxispos': 'bottom', - 'chart.yaxispos': 'left', - 'chart.noendxtick': false, - 'chart.crosshairs': false, - 'chart.crosshairs.color': '#333', - 'chart.crosshairs.linewidth': 1, - 'chart.crosshairs.coords': false, - 'chart.crosshairs.coords.fixed':true, - 'chart.crosshairs.coords.fadeout':false, - 'chart.crosshairs.coords.labels.x': 'X', - 'chart.crosshairs.coords.labels.y': 'Y', - 'chart.annotatable': false, - 'chart.annotate.color': 'black', - 'chart.line': false, - 'chart.line.linewidth': 1, - 'chart.line.colors': ['green', 'red'], - 'chart.line.shadow.color': 'rgba(0,0,0,0)', - 'chart.line.shadow.blur': 2, - 'chart.line.shadow.offsetx': 3, - 'chart.line.shadow.offsety': 3, - 'chart.line.stepped': false, - 'chart.noaxes': false, - 'chart.key': [], - 'chart.key.background': 'white', - 'chart.key.position': 'graph', - 'chart.key.shadow': false, - 'chart.key.shadow.color': '#666', - 'chart.key.shadow.blur': 3, - 'chart.key.shadow.offsetx': 2, - 'chart.key.shadow.offsety': 2, - 'chart.key.position.gutter.boxed': true, - 'chart.key.position.x': null, - 'chart.key.position.y': null, - 'chart.key.color.shape': 'square', - 'chart.key.rounded': true, - 'chart.axis.color': 'black', - 'chart.zoom.factor': 1.5, - 'chart.zoom.fade.in': true, - 'chart.zoom.fade.out': true, - 'chart.zoom.hdir': 'right', - 'chart.zoom.vdir': 'down', - 'chart.zoom.frames': 10, - 'chart.zoom.delay': 50, - 'chart.zoom.shadow': true, - 'chart.zoom.mode': 'canvas', - 'chart.zoom.thumbnail.width': 75, - 'chart.zoom.thumbnail.height': 75, - 'chart.zoom.background': true, - 'chart.zoom.action': 'zoom', - 'chart.boxplot.width': 8, - 'chart.resizable': false, - 'chart.xmin': 0 - } - - // Handle multiple datasets being given as one argument - if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0][0]) == 'number') { - // Store the data set(s) - for (var i=0; i 0) { - - this.scale = []; - this.max = this.Get('chart.ymax'); - this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; - - this.scale[0] = ((this.max - this.min) * (1/5)) + this.min; - this.scale[1] = ((this.max - this.min) * (2/5)) + this.min; - this.scale[2] = ((this.max - this.min) * (3/5)) + this.min; - this.scale[3] = ((this.max - this.min) * (4/5)) + this.min; - this.scale[4] = ((this.max - this.min) * (5/5)) + this.min; - - var decimals = this.Get('chart.scale.decimals'); - - this.scale = [ - Number(this.scale[0]).toFixed(decimals), - Number(this.scale[1]).toFixed(decimals), - Number(this.scale[2]).toFixed(decimals), - Number(this.scale[3]).toFixed(decimals), - Number(this.scale[4]).toFixed(decimals) - ]; - - } else { - - var i = 0; - var j = 0; - - for (i=0; i= (xCoord - offset) && - mouseCoords[1] <= (yCoord + offset) && - mouseCoords[1] >= (yCoord - offset) && - tooltip && - tooltip.length > 0) { - - overHotspot = true; - canvas.style.cursor = 'pointer'; - - if ( - !RGraph.Registry.Get('chart.tooltip') || - RGraph.Registry.Get('chart.tooltip').__text__ != tooltip || - RGraph.Registry.Get('chart.tooltip').__index__ != i || - RGraph.Registry.Get('chart.tooltip').__dataset__ != set - ) { - - if (obj.Get('chart.tooltips.highlight')) { - RGraph.Redraw(); - } - - /** - * Get the tooltip text - */ - if (typeof(tooltip) == 'function') { - var text = String(tooltip(i)); - - } else { - var text = String(tooltip); - } - - RGraph.Tooltip(canvas, text, e.pageX, e.pageY, i); - RGraph.Registry.Get('chart.tooltip').__dataset__ = set; - - /** - * Draw a circle around the mark - */ - if (obj.Get('chart.tooltips.highlight')) { - context.beginPath(); - context.fillStyle = 'rgba(255,255,255,0.5)'; - context.arc(xCoord, yCoord, 3, 0, 6.28, 0); - context.fill(); - } - } - } - } - } - - /** - * Reset the pointer - */ - if (!overHotspot) { - canvas.style.cursor = 'default'; - } - } - this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); - RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); - } - - - /** - * Draw the key if necessary - */ - if (this.Get('chart.key') && this.Get('chart.key').length) { - RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.line.colors')); - } - - - /** - * Draw crosschairs - */ - RGraph.DrawCrosshairs(this); - - /** - * If the canvas is annotatable, do install the event handlers - */ - if (this.Get('chart.annotatable')) { - RGraph.Annotate(this); - } - - /** - * This bit shows the mini zoom window if requested - */ - if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { - RGraph.ShowZoomWindow(this); - } - - - /** - * This function enables resizing - */ - if (this.Get('chart.resizable')) { - RGraph.AllowResizing(this); - } - - /** - * Fire the RGraph ondraw event - */ - RGraph.FireCustomEvent(this, 'ondraw'); - } - - - /** - * Draws the axes of the scatter graph - */ - RGraph.Scatter.prototype.DrawAxes = function () - { - var canvas = this.canvas; - var context = this.context; - var graphHeight = this.canvas.height - (this.Get('chart.gutter') * 2); - var gutter = this.Get('chart.gutter'); - - context.beginPath(); - context.strokeStyle = this.Get('chart.axis.color'); - context.lineWidth = 1; - - // Draw the Y axis - if (this.Get('chart.yaxispos') == 'left') { - context.moveTo(gutter, gutter); - context.lineTo(gutter, this.canvas.height - gutter); - } else { - context.moveTo(canvas.width - gutter, gutter); - context.lineTo(canvas.width - gutter, canvas.height - gutter); - } - - - // Draw the X axis - if (this.Get('chart.xaxis')) { - if (this.Get('chart.xaxispos') == 'center') { - context.moveTo(gutter, canvas.height / 2); - context.lineTo(canvas.width - gutter, canvas.height / 2); - } else { - context.moveTo(gutter, canvas.height - gutter); - context.lineTo(canvas.width - gutter, canvas.height - gutter); - } - } - - /** - * Draw the Y tickmarks - */ - for (y=gutter; y < canvas.height - gutter + (this.Get('chart.xaxispos') == 'center' ? 1 : 0) ; y+=(graphHeight / 5) / 2) { - - // This is here to accomodate the X axis being at the center - if (y == (canvas.height / 2) ) continue; - - if (this.Get('chart.yaxispos') == 'left') { - context.moveTo(gutter, y); - context.lineTo(gutter - 3, y); - } else { - context.moveTo(canvas.width - gutter +3, y); - context.lineTo(canvas.width - gutter, y); - } - } - - - /** - * Draw the X tickmarks - */ - if (this.Get('chart.xticks') && this.Get('chart.xaxis')) { - - var x = 0; - var y = (this.Get('chart.xaxispos') == 'center') ? (this.canvas.height / 2) : (this.canvas.height - gutter); - this.xTickGap = (this.canvas.width - (2 * gutter) ) / this.Get('chart.labels').length; - - for (x = (gutter + (this.Get('chart.yaxispos') == 'left' ? this.xTickGap / 2 : 0) ); x<=(canvas.width - gutter - (this.Get('chart.yaxispos') == 'left' ? 0 : 1)); x += this.xTickGap / 2) { - - if (this.Get('chart.yaxispos') == 'left' && this.Get('chart.noendxtick') == true && x == (canvas.width - gutter) ) { - continue; - - } else if (this.Get('chart.yaxispos') == 'right' && this.Get('chart.noendxtick') == true && x == gutter) { - continue; - } - - context.moveTo(x, y - (this.Get('chart.xaxispos') == 'center' ? 3 : 0)); - context.lineTo(x, y + 3); - } - } - - context.stroke(); - } - - - /** - * Draws the labels on the scatter graph - */ - RGraph.Scatter.prototype.DrawLabels = function () - { - this.context.fillStyle = this.Get('chart.text.color'); - var font = this.Get('chart.text.font'); - var xMin = this.Get('chart.xmin'); - var xMax = this.Get('chart.xmax'); - var yMax = this.scale[4]; - var gutter = this.Get('chart.gutter'); - var text_size = this.Get('chart.text.size'); - var units_pre = this.Get('chart.units.pre'); - var units_post = this.Get('chart.units.post'); - var numYLabels = this.Get('chart.ylabels.count'); - var context = this.context; - var canvas = this.canvas; - - this.halfTextHeight = text_size / 2; - - - this.halfGraphHeight = (this.canvas.height - (2 * this.Get('chart.gutter'))) / 2; - - /** - * Draw the Y yaxis labels, be it at the top or center - */ - if (this.Get('chart.ylabels')) { - - var xPos = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : canvas.width - gutter + 5; - var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right'; - - if (this.Get('chart.xaxispos') == 'center') { - - - /** - * Specific Y labels - */ - if (typeof(this.Get('chart.ylabels.specific')) == 'object') { - - var labels = this.Get('chart.ylabels.specific'); - - for (var i=0; i= 5) { - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (1/10) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align); - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (3/10) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align); - } - - if (numYLabels >= 3) { - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (2/10) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align); - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (4/10) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align); - } - - // Draw the bottom halves labels - if (numYLabels >= 3) { - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (1/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align); - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (3/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align); - } - - if (numYLabels == 5) { - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (2/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align); - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (4/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align); - } - - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (5/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align); - - } else if (numYLabels == 10) { - // 10 Y labels - var interval = (this.grapharea / numYLabels) / 2; - - for (var i=0; i= 5) { - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (1/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align); - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (3/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align); - } - - if (numYLabels >= 3) { - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (2/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align); - RGraph.Text(context, font, text_size, xPos, gutter + ((canvas.height - (2 * gutter)) * (4/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align); - } - } else if (numYLabels == 10) { - - // 10 Y labels - var interval = (this.grapharea / numYLabels) / 2; - - for (var i=0; i 0) { - angle = -1 * this.Get('chart.text.angle'); - valign = 'center'; - halign = 'right'; - yPos -= 10; - } - - for (i=0; i= 2) { - - this.context.lineCap = 'round'; - this.context.lineJoin = 'round'; - this.context.lineWidth = this.GetLineWidth(i);// i is the index of the set of coordinates - this.context.strokeStyle = this.Get('chart.line.colors')[i]; - this.context.beginPath(); - - var len = this.coords[i].length; - - for (var j=0; j{XE z)7O>#8Y?HeiPce~l`DWkvL&t&CC>S|xv6<249-QVi6yBi3gww4844j8sS56%z5(x3 pRP%rec|2VlLpZJ{|M+}Ko`F@8$+T(TnfpL-22WQ%mvv4FO#nkKCNls4 diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/chameleon.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/chameleon.png deleted file mode 100644 index 68046070133a2c309839b9d340ced4c521fa9d3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryoCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#8Y?HesrbxqN9F*9WJ_ElN}Tg^b5rw57@Uhz6H8K46v{J8G895GQWe}ieFNU7 psOA9`@_4#9hHzX@eo>Xe#K1g@abe4bR%xI(gQu&X%Q~loCID3dC3gS- diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/close12_1.gif b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/close12_1.gif deleted file mode 100644 index e2f67d72efc158da4e069822cbe338915761e396..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85 zcmZ?wbhEHbz lf$Gy9znh?Y?^x%Bps9Cfw!UsJn!aoAS>7kV)`>G%0|4uCA&me4 diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/container-min.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/container-min.js deleted file mode 100644 index eadb7f2..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/container-min.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright (c) 2010, Yahoo! Inc. All rights reserved. -Code licensed under the BSD License: -http://developer.yahoo.com/yui/license.html -version: 2.8.2r1 -*/ -(function(){YAHOO.util.Config=function(D){if(D){this.init(D);}};var B=YAHOO.lang,C=YAHOO.util.CustomEvent,A=YAHOO.util.Config;A.CONFIG_CHANGED_EVENT="configChanged";A.BOOLEAN_TYPE="boolean";A.prototype={owner:null,queueInProgress:false,config:null,initialConfig:null,eventQueue:null,configChangedEvent:null,init:function(D){this.owner=D;this.configChangedEvent=this.createEvent(A.CONFIG_CHANGED_EVENT);this.configChangedEvent.signature=C.LIST;this.queueInProgress=false;this.config={};this.initialConfig={};this.eventQueue=[];},checkBoolean:function(D){return(typeof D==A.BOOLEAN_TYPE);},checkNumber:function(D){return(!isNaN(D));},fireEvent:function(D,F){var E=this.config[D];if(E&&E.event){E.event.fire(F);}},addProperty:function(E,D){E=E.toLowerCase();this.config[E]=D;D.event=this.createEvent(E,{scope:this.owner});D.event.signature=C.LIST;D.key=E;if(D.handler){D.event.subscribe(D.handler,this.owner);}this.setProperty(E,D.value,true);if(!D.suppressEvent){this.queueProperty(E,D.value);}},getConfig:function(){var D={},F=this.config,G,E;for(G in F){if(B.hasOwnProperty(F,G)){E=F[G];if(E&&E.event){D[G]=E.value;}}}return D;},getProperty:function(D){var E=this.config[D.toLowerCase()];if(E&&E.event){return E.value;}else{return undefined;}},resetProperty:function(D){D=D.toLowerCase();var E=this.config[D];if(E&&E.event){if(this.initialConfig[D]&&!B.isUndefined(this.initialConfig[D])){this.setProperty(D,this.initialConfig[D]);return true;}}else{return false;}},setProperty:function(E,G,D){var F;E=E.toLowerCase();if(this.queueInProgress&&!D){this.queueProperty(E,G);return true;}else{F=this.config[E];if(F&&F.event){if(F.validator&&!F.validator(G)){return false;}else{F.value=G;if(!D){this.fireEvent(E,G);this.configChangedEvent.fire([E,G]);}return true;}}else{return false;}}},queueProperty:function(S,P){S=S.toLowerCase();var R=this.config[S],K=false,J,G,H,I,O,Q,F,M,N,D,L,T,E;if(R&&R.event){if(!B.isUndefined(P)&&R.validator&&!R.validator(P)){return false;}else{if(!B.isUndefined(P)){R.value=P;}else{P=R.value;}K=false;J=this.eventQueue.length;for(L=0;L0){G=F-1;do{D=E.subscribers[G];if(D&&D.obj==I&&D.fn==H){return true;}}while(G--);}return false;};YAHOO.lang.augmentProto(A,YAHOO.util.EventProvider);}());(function(){YAHOO.widget.Module=function(R,Q){if(R){this.init(R,Q);}else{}};var F=YAHOO.util.Dom,D=YAHOO.util.Config,N=YAHOO.util.Event,M=YAHOO.util.CustomEvent,G=YAHOO.widget.Module,I=YAHOO.env.ua,H,P,O,E,A={"BEFORE_INIT":"beforeInit","INIT":"init","APPEND":"append","BEFORE_RENDER":"beforeRender","RENDER":"render","CHANGE_HEADER":"changeHeader","CHANGE_BODY":"changeBody","CHANGE_FOOTER":"changeFooter","CHANGE_CONTENT":"changeContent","DESTROY":"destroy","BEFORE_SHOW":"beforeShow","SHOW":"show","BEFORE_HIDE":"beforeHide","HIDE":"hide"},J={"VISIBLE":{key:"visible",value:true,validator:YAHOO.lang.isBoolean},"EFFECT":{key:"effect",suppressEvent:true,supercedes:["visible"]},"MONITOR_RESIZE":{key:"monitorresize",value:true},"APPEND_TO_DOCUMENT_BODY":{key:"appendtodocumentbody",value:false}};G.IMG_ROOT=null;G.IMG_ROOT_SSL=null;G.CSS_MODULE="yui-module";G.CSS_HEADER="hd";G.CSS_BODY="bd";G.CSS_FOOTER="ft";G.RESIZE_MONITOR_SECURE_URL="javascript:false;";G.RESIZE_MONITOR_BUFFER=1;G.textResizeEvent=new M("textResize");G.forceDocumentRedraw=function(){var Q=document.documentElement;if(Q){Q.className+=" ";Q.className=YAHOO.lang.trim(Q.className);}};function L(){if(!H){H=document.createElement("div");H.innerHTML=('
    '+'
    ');P=H.firstChild;O=P.nextSibling;E=O.nextSibling;}return H;}function K(){if(!P){L();}return(P.cloneNode(false));}function B(){if(!O){L();}return(O.cloneNode(false));}function C(){if(!E){L();}return(E.cloneNode(false));}G.prototype={constructor:G,element:null,header:null,body:null,footer:null,id:null,imageRoot:G.IMG_ROOT,initEvents:function(){var Q=M.LIST; -this.beforeInitEvent=this.createEvent(A.BEFORE_INIT);this.beforeInitEvent.signature=Q;this.initEvent=this.createEvent(A.INIT);this.initEvent.signature=Q;this.appendEvent=this.createEvent(A.APPEND);this.appendEvent.signature=Q;this.beforeRenderEvent=this.createEvent(A.BEFORE_RENDER);this.beforeRenderEvent.signature=Q;this.renderEvent=this.createEvent(A.RENDER);this.renderEvent.signature=Q;this.changeHeaderEvent=this.createEvent(A.CHANGE_HEADER);this.changeHeaderEvent.signature=Q;this.changeBodyEvent=this.createEvent(A.CHANGE_BODY);this.changeBodyEvent.signature=Q;this.changeFooterEvent=this.createEvent(A.CHANGE_FOOTER);this.changeFooterEvent.signature=Q;this.changeContentEvent=this.createEvent(A.CHANGE_CONTENT);this.changeContentEvent.signature=Q;this.destroyEvent=this.createEvent(A.DESTROY);this.destroyEvent.signature=Q;this.beforeShowEvent=this.createEvent(A.BEFORE_SHOW);this.beforeShowEvent.signature=Q;this.showEvent=this.createEvent(A.SHOW);this.showEvent.signature=Q;this.beforeHideEvent=this.createEvent(A.BEFORE_HIDE);this.beforeHideEvent.signature=Q;this.hideEvent=this.createEvent(A.HIDE);this.hideEvent.signature=Q;},platform:function(){var Q=navigator.userAgent.toLowerCase();if(Q.indexOf("windows")!=-1||Q.indexOf("win32")!=-1){return"windows";}else{if(Q.indexOf("macintosh")!=-1){return"mac";}else{return false;}}}(),browser:function(){var Q=navigator.userAgent.toLowerCase();if(Q.indexOf("opera")!=-1){return"opera";}else{if(Q.indexOf("msie 7")!=-1){return"ie7";}else{if(Q.indexOf("msie")!=-1){return"ie";}else{if(Q.indexOf("safari")!=-1){return"safari";}else{if(Q.indexOf("gecko")!=-1){return"gecko";}else{return false;}}}}}}(),isSecure:function(){if(window.location.href.toLowerCase().indexOf("https")===0){return true;}else{return false;}}(),initDefaultConfig:function(){this.cfg.addProperty(J.VISIBLE.key,{handler:this.configVisible,value:J.VISIBLE.value,validator:J.VISIBLE.validator});this.cfg.addProperty(J.EFFECT.key,{suppressEvent:J.EFFECT.suppressEvent,supercedes:J.EFFECT.supercedes});this.cfg.addProperty(J.MONITOR_RESIZE.key,{handler:this.configMonitorResize,value:J.MONITOR_RESIZE.value});this.cfg.addProperty(J.APPEND_TO_DOCUMENT_BODY.key,{value:J.APPEND_TO_DOCUMENT_BODY.value});},init:function(V,U){var S,W;this.initEvents();this.beforeInitEvent.fire(G);this.cfg=new D(this);if(this.isSecure){this.imageRoot=G.IMG_ROOT_SSL;}if(typeof V=="string"){S=V;V=document.getElementById(V);if(!V){V=(L()).cloneNode(false);V.id=S;}}this.id=F.generateId(V);this.element=V;W=this.element.firstChild;if(W){var R=false,Q=false,T=false;do{if(1==W.nodeType){if(!R&&F.hasClass(W,G.CSS_HEADER)){this.header=W;R=true;}else{if(!Q&&F.hasClass(W,G.CSS_BODY)){this.body=W;Q=true;}else{if(!T&&F.hasClass(W,G.CSS_FOOTER)){this.footer=W;T=true;}}}}}while((W=W.nextSibling));}this.initDefaultConfig();F.addClass(this.element,G.CSS_MODULE);if(U){this.cfg.applyConfig(U,true);}if(!D.alreadySubscribed(this.renderEvent,this.cfg.fireQueue,this.cfg)){this.renderEvent.subscribe(this.cfg.fireQueue,this.cfg,true);}this.initEvent.fire(G);},initResizeMonitor:function(){var R=(I.gecko&&this.platform=="windows");if(R){var Q=this;setTimeout(function(){Q._initResizeMonitor();},0);}else{this._initResizeMonitor();}},_initResizeMonitor:function(){var Q,S,U;function W(){G.textResizeEvent.fire();}if(!I.opera){S=F.get("_yuiResizeMonitor");var V=this._supportsCWResize();if(!S){S=document.createElement("iframe");if(this.isSecure&&G.RESIZE_MONITOR_SECURE_URL&&I.ie){S.src=G.RESIZE_MONITOR_SECURE_URL;}if(!V){U=[" - - - - - - -
    {name}{methods_bar}
    {methods_tested_percent}
    {methods_number}
    {crap}{lines_bar}
    {lines_executed_percent}
    {lines_number}
    - - - -
    {title}
    - -
    - -
    - - - - - - - - - - - -
    -

    Class Coverage Distribution

    - - -
    -

    Class Complexity

    - - -
    -

    Top Project Risks

    -
      -{top_project_risks} -
    -
    -

    Least Tested Methods

    -
      -{least_tested_methods} -
    -
    -
    - -
    - - - - -
    Generated by PHP_CodeCoverage {version} using PHP {php_version}{generator} at {date}.
    - -
    - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.html.dist deleted file mode 100644 index fda0ed1..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.html.dist +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - {title} - - - - - - - - - - - - - - -
    {title}
    - - - - - - - - - -
    Current directory:{link} ({dashboard_link})
    Legend: - - Low: 0% to {low_upper_bound}% - - - Medium: {low_upper_bound}% to {high_lower_bound}% - - - High: {high_lower_bound}% to 100% - -
    -
    - -
    - -
    - - - - - - - - - - - -{total_item}{items} -
     Coverage
     LinesFunctions / MethodsClasses
    -
    - -
    - - - - -
    Generated by PHP_CodeCoverage {version} using PHP {php_version}{generator} at {date}.
    - -
    - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory.png deleted file mode 100644 index 65bd0bbdcb9005cb8929f06e25d9cb15a926366a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 581 zcmV-L0=oT)P)2|yPkE-3> z-8b(3_?ov1cYiV%432IDm|3sa>)kUm%nWx2;PmwLe!t&8>~uQMm(4k6LI`Cz9Dc0p zx&Tx;XIE7wlL@7i6(oef_V#uMc)NrGOsCVTl=7;s>w_zRjH+U0Ymj!k-P+vTq}6H> zV^0g%)=c>R8-N-3GD0!|F@>GqJ z%4)bdf_Rm4wwyD#6GYg!0ieZcvQf+lK>!v2)x`$$R0x3iqPe(Z=9oKK7jS;E^w*b55oWe`mg~7=)i^mlj0C%nT8s$wuo= zb!&(s!bMVmQn@?FCx!@o{yMsKc&XWJj=)i68(aN%$9JEC^W*#Yp8+C_MZ$~UM;0NT T9Al?300000NkvXXu0mjfM@0T3 diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory_item.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory_item.html.dist deleted file mode 100644 index a0065f9..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/directory_item.html.dist +++ /dev/null @@ -1,31 +0,0 @@ - - {icon}{name} - - - - - -
    {lines_executed_percent}{lines_executed_percent}
    - - {lines_executed_percent} - {num_executed_lines} / {num_executable_lines} - - - - - -
    {methods_tested_percent}{methods_tested_percent}
    - - {methods_tested_percent} - {methods_number} - - - - - -
    {classes_tested_percent}{classes_tested_percent}
    - - {classes_tested_percent} - {classes_number} - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/excanvas.compressed.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/excanvas.compressed.js deleted file mode 100644 index fc3d35c73606b2cdcea1cfb77aa125f90251d04d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8660 zcmV;_AuHY=iwFotV;)NY17&z)VQzL|b1r9kX>f35WG-rR0OUJqbKAy}-}x&Lr(%K@ zMH8S%@raV*`Rpw_Nm+i09j%r%1coGRTn0c%w4eX`cK6Hx1CWxOyn0nzwK_;lcTZ1W z({sXG8|HUz_-W-TY-EaP*ZW67*LEWBZ9z0un6Y#N0bzsnlNmzufWg-+&0p^2p% zmKcF?VZ3J9b|gv(6oXb0?Lt>VQi7yjD9a9&ohTJ$wh{#JEdc4KBokHOL4f{*velN) zHWdCeo*T9(znm|BpTD_$`RgwLKMC25w_#b7w00>^#v9ZuV41TupTE9*IXBF!|8`r~ zCyk$Dg&WDo zo-K*G(HPaf)`V)l=WwVrP7=0n#YvP!**@kif@{L8ywEk*O(R-;Xc|y)$&yW~49QLGGW_c6%%Q`#((_!jtdBD=s zz;lu)jn>(z69q${K*^5QjvlESt=Hgg`Gs6^d!{JfC5o3`)%o30B!Ff8TE$(8{S+xh zrnt=MO<`v{0FVi!<+(tu^C221P2WP-K$L)galI76Vn6gk-wXNa%8#6nqv(bw zYd^XjZavrK;izt$)aux$PMPm*!lBRC+341Dv+c0cZpU|{E%!FtY-n|%@U<>{@v!|V z&>JF!`~!_HI`L~3vJFqLXG1WBUN$VcQWR%P={c+gk3c1XkQ)rebyO>?bdXO4l4Til zw(}c8MIn&7`!oZ3XUoD32Om-jpk#wmESr>yn%D;Q&1Q|HTWaEIqCv+ssom`3+1QC5t zjtShS;z-MiRl}RS2wLRUN)tzh5>!iXJs7(Uled%yF*2}*!$6WcnW8={hm+X=i8xE% zaEEf!fvHPwyVvAEwf2%Un{GYdt#GK=KCiQap8Rnng%c+FX6bZ`YU17NjbrlN=as5} z=p)S~BDx=R1%_6sMHxYV*+hR!Y?Sv%mZp?s$}H}3=WX*JZt>g5?NM}H9L z4-)-BpcS(#&v*uK5ah!(5PGdFk`o=P*Fb0qP%*3+0$_5Scy}*h9Lv0HuTRLXYFAd% zXv5!ivvvjFR`H0X>)Q25=NAyC*DyQra2^)`d0;D_L9zk_$auU}%^-I|<2%r}178(; z>;-l&uzP`}@qZT(s#ef`7POxQ?Po!YbSE%>x32E4uRdRc{Q?`lulA&j5ED^*G$;_4 zGKN5xw05F+e~DwG8G*tPDID1lM7~cWgMmLV5L*RkgakOBn6lU~3IYkr(f<{BVFit# z94{ibgqOJ%2m&wT$tw@Oa)JUx6zP_^(e1?FB{@X_(kP~pA0=f90%;__uGcBg-svO~ zbU^g}km$W2%97|KS2hzw1&ByCezao#*pIhNk!TxCqNio7`+bKc*ZhDlRbv( zpcN$8p;7;S(O&cyi+Z!hp! zIIJL(2}pO*olOzqrJrOoi}~3UtF2&V^bsL#`Vo*2X7DcagD7+*@nkw9)Q;bHksS!fPU3+Yg5wc7C{cRLc_1S#V@1x`nb9tr|?JfrMF}4RYqa;f>teTZR zZ2^wJOiU<0vwH(;j?gG!zL4pB(QOMzh%vUOUIYYMTr*z~3Cmxn$Bi zBV8r*ko4o0ee{UyY;xXHTmy!?3ygd)(S(b9?}q2xyWQTzR?2|cLKzH;{xokiN*q|| zAb2q7*wd*5C)`!cbJCRJ0m1XZpm#o2V9xRu`-{o6uUbsecglum{mv{GlQ7CM_TmhF z%NTx$ffNm^b}yA4na*dH#1!#30AtQN2$M0?G&E%@A186jS>xHP zKkq4MkwTPV1X%X$xyk_q%}ra_4O}^%**SrdY0#cSOlV*-z^sG63R+ln(wkZeTJ$WC zzD+$#z_qSrw#;hZZ5ef)KIvHEbQkc@orU7@oT~HpudcEn4R1-)&?UE}6xX4tKakon*6^MA3 z#6Hy+bk5teNsiFX*O^W_=T?EzbJO{x*PqV|T#R#SEk`dPALrOo9y;sKEXtVzCo+-h z9_LoKkAWL_Zm0*NHLwQfZ9;?@&X@x^L@&sVfd>ndBuev9dQM_T4y^3FPyr{%Ib=8N zL!>2{*?f!!gOJ7TPJ+!AU2+Bu4Q|3#WbsLVZleYDQDNMr9wk6hiN)efL6!AqV=yvu zjA24kN<8!Vyia~4Ox`VEP#E+jv}C>U0&2`9rer%9>)MpFV-~YL0}W%T_+o~N$DBFa zc(-0lwa1eIL6e{Z0#n35H43+bmXZ#JtJsZX0EVP=Zm zXj)k3>(7kQ!H|Z6^}oySeqEb&v)(j1aw7o7uKUOuKqmN{7?vcDp_o=Gq`Orr&odNt z6Ztk;Y3zF$w5ImXskYoMHNLwr&TuDKXN42%ab|WJh^@0asza5oI=BF~M#bWnz~%E0 z=uQsVL)$h0vXn3Uh!Oa?VYS=kN{-Kh*t}^Pklf&d-D#Eas1DYtp$XuoEU`P2C~q2- z)0l1Qzm32#Ld%BSXlNRnrm<=oTd-x*@JBV=?$Lx2+O@Aywob2Y{D7arT3h2squr=& zPfkWPw7HN}DJd#i*A21#0mAtA(AXLc@&#TOz!=rU&1Cpr!N0FxK*=twUjr6NN|PqeLecMFfj}8&W^b!%vy~M5VxreIVof`}PsGsU)va-u6M$ z%5C|E(3QsTx{}bfeEo$g47Feyim$I~i)jX{f`7hxyS#k+{?(kenTQh%gJF~e%*T-x zrr-?E%?i-1IL*``xII6FL=S6M7I;3+6JQ>bo~AtU)-;r2Yq$?b5kG~Q8QwGaqK~n% zODq&owOgPA${b2&&@U3)Po)&66NLNjmbl(cJr85tjiinLRO~`#$zGXB>@^;ACx?kM zAsW`ceuGfD;S0uMBNUula*_~3b4yX-cL6%AB54c96?Og)=NJc71f_DUCKQy9$OO>B z^#%2UAcypVQrzGu-*AD2TE<l+sq_*XUMSk9GdP{}D!x{(g2#N1 zmCkDxO0&{DK{niSc1gmeKXM~9aGT~>Eup%6Na z*>BJm!{!LtGZ@rbgFcSavbUFr%0vHZ=lObsYl%y0M3H@B)D4_*37G7{73gh#3W>m) z7GiWTsZoj^cXST&N*p?w^6-gf3G!H51BoN!)h@#WV>KmQ!@97pj+5z5orV-;g-t*0 zSi%7q*wy#LOc@W{N#3Uv7+SUzyHC@3y7=6qbr<5vQ)Ly+w3zrkFi1lJFz zF2u%DU80JUz6zo^qN6!n4x?i|przwDofwY;FQk=~)WsD` zXgBH)NtUxwu`h?RB?+j4zc|M%;zv8WEXS-ot&|%AVj!k2`J1{W3b=baZE=?r#IDpU zuA5+T*6)SV=&7RfHD~EA0oN__K3@j65*~o!WcV!E@y6&F6<&$m`E>86WSN6YT6iny zK-3lAcp>J7s;Kwnj;}}bMJeA#ItTU+=I8J8RRo|!7snYhJ{HMegy#FI&ulQ$=C(~VWVD#y4 z7PN$4`(_QkY*DhY^zE8aSBR}+Bw7PGp?#b%ZyUE{bcs!IuM%3o54&l^;_7oTohVU5 z249z{aX1IyG2_>y^-C=9pgXp}5AX?|z`|Y>_E@k9V+)ME7X`pb$JK=6YUnw=*}`)= zRP)@S<6v6gCJaWPZagU-9S{RI3tzCx+c><4zBJK7$!6C}o^h zh#F*vN{k*>+*c~TUpCZISp*8TPkz9?rlTzhhSVPUNDv<3EC?yBeN%uP_ydN9!_E)? zZ!mc)mE<^O{!_kSkvMO~@KI12Kf{QZ)eQ*F zTPDZ$yGyyj&fbBNfn#b7;u=A5J~jT7uU_o)8t<`2`go1IIql8oEhsJS;V-Eb_xq`F zBvbNVh*q9HHNGpGMiQLhV#2b!`CA!}25YSJtWpV%O=$>4Eu=o61#pv8!dt`-=WFS* zYOinB5>k3t1ci`bC`BaFG6Yk{94`=|ScET1l>d*$Vp5qc<-_5aLTsPf;km00cI0-q)Sz`n`Dirv~M1Ae@-@X1mp+Z5Er z8)S;b(yS{yuAbC$>#n_|djWf|!|p6WXX$hT_TI{05mTs#RKqmG{6|!K)5Zy% z&<~4$S;>T2`CFY6c+p~amqqoXB>7Ez zMS~6B<}GQ`6!LyUixpKLZn~Rr%P(T+ywx#XQV-0q{y+LAMfbh798Yw_$N5xVSJub|E)*gq#q(`(r6ec|s zrlMH)P_f>jV&{j7_4T8*Y@0;4#ym-)#H_!B4B&gNA$I4DXkFA2Z-P?aIWl_^{YlQS zZgK7oD&_A0Zwat105^euNk@9PM}UAojN%RA7wVaa#Kfy^{3u?%9OAxF42!h7;ZWP& zXMXVnR?fIL_(#*IH|6sScz#mQMTrMP+{Kf2v;CxQAeeTTE}qB@yCzlEw&j|{0RRVk zZ6kHJL~guZwXfk1cik;K>d~MU9!{$LStuc(N@iWtRSc1q^8GG)Lxu`c|JRmvQEwWh zCWW>&z}Y&^D&g#EJxjw?>lL`!9ioE7gh?s*?axMz*yo77La5WifMJBn7t5VvOMMqt za+GxQErHIx;e@1HMRxWuH$@8~v`~mZT${lXtm|z0?9k^~>rT?`OLTjp+Y{ZM=tx98 z2xI2DUbq>y4PgS&m^1>OH{?)hni@jeBjL$moJ1SX9nRj-DD^gBVY-fzp!Hh40^AZ? z_a@z#Cg*>MywC*uHjO7RFnOP$^?+yF$i1jLz88bxpXi7FC26xL9v-*~0vh+dhX>9D zgzVy_ZujIT>Mg4G(YRowiSxg$U2St3M-u+duUM<<3L$~Sa!D>HBv%oHVyYye1C&JP zN>Nx^pOgr}u_P9i(tp2sx_f$Nb_Z;qUH)Kpx~J#8XZlri=J=K{UwYPxhYBYV3q<6B z;Eor8?g=F4bU9z3Z$vsr$3D0-Y+zgh@3eT1^I_siNw44YBirR zBHVm(C<^lXT0rpdHU2w3^cl|Us!D~P#x&22G=2Bm6uuZxo$gkD$l$zggg4WIOr%}H zt@2OsbUQQeUCR+j_Z+ZFlnfRbOcX*U-O+TsdPU6^ z7qZO!KFTjQG2PE$**q!Iv#?_e^V&D%!WPWCb(HWMO%GTfo5q@T(4v%3}ssfu6=HmB z08y+2N<7&roF%{AAR3pT9sI-z2!A{Ve<#ZXy? zRo2qV+P1QGnsD1nsS3BUwyk(o@UO7oJFhi;hB8#P5sp~-!g^n+n?_5*$fib9SAk8mVV9Oql0SZ0cEr~J$)xQAGx@CQ+Jc0eI?9gY8@ z!3=YA_=ReCT~um;DSt0<#+d`%$?*{H6fE5x7+b@Cj}( z7Z3r?%Km)r7`EmmcD=$HOrLJy?N!hv=^ll7fQ4yxh`jwHm)p2WS8-Er>HPlT{mLO` z&kOTd#$=HJK4_M>`h=XyoK{ZSdv4-spqS8F9lCiED@KU34sdS7E|P|H(ioi(XPrjq zESfU+zTp8QZ(8F-8bL@6f{lhC9ROLx8itx(A5sI5`HM=;%GjAEr{SqtmX?*#;Y0I1 z3~>ay>Wlh@@Q>L^pFj3bI56NPKuEL3cMgYE=p}UHj}00z70xT-K7?<6D z^oFNn1tSYUy8YapU2^^&j=paYpE0qpza~LY~mghES ziVo(KNWjoGAo*yM6`I+$D5bbD`-XM*L25y7o4@+^0{v_$P{=fr^w-Gk5fIsWi%5ky z;f*z2kMd-54w1|@lFKuqUUhd>wYO!1$DTE@*IC8ydfbwk#Qey_Ix0?VQR|nr5k~88 z_OoIs_hkcBcVCr_tA097QJAV(if%0^Fkt)KOJ<@F-iLV;pQ(J#-nLlT3#SOW&kB{& zuu`g(QmI5C12HAi^v|p~6|~X`2MD+vJuK18t#~3Hz&uM@I`d0qv6!$w; z&GLrnMv@03{uKMInswVZ=IHFt?hnIte!j4+1-%T%0whUyR>;`Y&(DykD%+iTm+FHS z@^aR|dcwk#3M*b=8pBLun5`HluXG?Bm}}{R#e!Kq`D1Y>_H5!Mn!c|z3^xvPErYB> z%f@fL2pQ&>^um|j>-JznH>YmqcBLTvolsiX-RZl0*N~}gI`S*%yb(;j<$&QC!I`%p z6KKM%B()#b+kx|M(FF0Xj^y96JFuQuU*zacoUe*~8qQtB4QAz*i$pp>N$!`}Uf-Hd z=8`3>0k<-jG0de5`x;%&G4JtNEa{$LY%lvaqxdH5J!k}Tj%oJ$|Cs~W!ogS8`r(gZ zmLk+OZ6V&3f?mxpE475X!(meQ96EiX4w?7AUVjaV@&DQ7FYWQxe!=NsEV+)gQ$VRF za3d!Dfv6?;T8&&}@_HeoL9>Z`F)Vx6D@zgk30|+rGdUF4>y@&N!o}~Ml;he;fsPuv zQLBeavs!_g#{YUTL<+Ro1-PzlJygQ^p=^xqM}&kSA_uGaITB`dr#X3QStY(AI6Xc^ zl349C)O*=dKbMkZGEi1$6e;Al?RWd&?g0*dBugSrWworrA(Sb>CsZO=0+N&n;nRsO zSC3nXYM4?iWah<9MlpECO$yS7n$^;YF!Gc6&0C0cx4Lp1vUQlG6pt2+YW9G{A#VD# zTLgx3*pl}mc5KnZT{i#Ea=N(p%Gd%JcBE12N$a5Xt*0~R(Z*KZuC5#zIwx&CHqC=( zaX`8|q(*SU*g-;@-Q;Wm1A}xQXWV_kU>$$AQ(V*grPw|j)Ofv`Gy<%5IKG$UnPiy6*~4>ohx{S?kOb+)o^AkDY0suhvq#O3cZO4C%OhynxE|#{YEa(tqs1~aeC7BM&AqOE z?pmvySqiQT={!y)k+QOLN=hFh1reH2x~1T}7*3FqL7!A{Tt8USCBw(UarhustmgC^ z%$1WECFT{hH#X=d;HOlS@i)ZRgh5W55V^CW9!$!3V-=w6QF zM_zLM{3m#%GM@Je4=c!H%#=Ad5dU$AcQH+^*209^3i9L%s5GA43!V$sTJSV)Xj2ig z?kn<|hPQ3+`8He7(o{z`m*dValiqO1YEssJE^k3cz3XH_=oC7D_<9qFA4?8qUA{d< zQlaI%roO*88mEH`g-v$zf`K}=9Ga8nmH7=14f7vm!KYw0>*b2V75`Za%CFYH__3-j zd-E{9x#~?W`lE}^xckOU2k3(Y{Q(Zr{1Fvomw3!pS(H@&@~6&u|8z1Mcg8(O${SB{ zu&OsUy%*m3Hn)inX6ATsOhKOPSC3FR(b{Ag(z=$(xI>z7s0F!Ow=0W-{^e=! zm&vIAZ|W&qw-*Pd@@PElU;gMrT7bOi_lBKe_sz{@*!$OYZ!{L*-vRu2I51UC2i@z7 z-lc_n!_DxVbl-G_o$k0d6yUc2UJfRwopEQ7xRuE#p)rr$|jI6!DV+$lj(%SR5n zCkQkdmz|5==&I9IO2I^Qy4~JrWI;3y1PJKR6qI%*AZFy{KX+gNk-ve^u58K*kZeg9 m&@S+K#Z|^{G2A^Ea@Kam--5**3EMEK!~X|ZcU7NdYybd{5z+Vn diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.html.dist deleted file mode 100644 index a1a5766..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.html.dist +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - {title} - - - - - - - - - - - - - - - - - - -
    {title}
    - - - - - - - - - -
    Current file:{link}
    Legend: - executed - not executed - dead code -
    -
    - -
    - -
    - - - - - - - - - - - -{total_item}{items} -
     Coverage
     ClassesFunctions / MethodsLines
    -
    - -
    - - - - - - - - -

    -
    -{lines}
    -
    -
    - - - - -
    Generated by PHP_CodeCoverage {version} using PHP {php_version}{generator} at {date}.
    - -
    - - - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file.png deleted file mode 100644 index 2d7f2d6017d823bf9f1209d9933faf612dffe9f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP$ic-+2*wv2(<2J_t+h+ zs5rlI{^p#SO2-|aiODDi-jZP}W|aE1)~BmOR3$EA(Vi@w>U%ZDa$9qM8}c0HKK>=@ z`d4d)YqwsmShMx=n<-}wTJ%-CXPi)?Z+d)E$Kx3$zTq0>%B>G-<`ESK<06 zKLv;!Rde(>`RU=L2g~Ovo%n1M@bQ?*+>S*Vvl$k=|9v$za@qmb9gNTaiaD`NnxMxo zp5*#q&TW}{<`>WDw{ULpSLj^#x@qe3m+d`GEQ*uw+Md|dvtPsTSdE!)vF)Ukp32Aj azp#b`#_#;I)BX+6mkge+elF{r5}E*#afeO- diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_item.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_item.html.dist deleted file mode 100644 index 938db30..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_item.html.dist +++ /dev/null @@ -1,32 +0,0 @@ - - {name} - - - - - -
    {classes_tested_percent}{classes_tested_percent}
    - - {classes_tested_percent} - {classes_number} - - - - - -
    {methods_tested_percent}{methods_tested_percent}
    - - {methods_tested_percent} - {methods_number} - {crap} - - - - - -
    {lines_executed_percent}{lines_executed_percent}
    - - {lines_executed_percent} - {num_executed_lines} / {num_executable_lines} - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_no_yui.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_no_yui.html.dist deleted file mode 100644 index 9bdb84f..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/file_no_yui.html.dist +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - {title} - - - - - - - - - - - - - - - -
    {title}
    - - - - - - - - - -
    Current file:{link}
    Legend: - executed - not executed - dead code -
    -
    - -
    - -
    - - - - - - - - - - - -{total_item}{items} -
     Coverage
     ClassesFunctions / MethodsLines
    -
    - -
    - - - - - - - - -

    -
    -{lines}
    -
    -
    - - - - -
    Generated by PHP_CodeCoverage {version} using PHP {php_version}{generator} at {date}.
    - -
    - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/glass.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/glass.png deleted file mode 100644 index e1abc00680a3093c49fdb775ae6bdb6764c95af2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)gaEa{HEjtmSN`?>!lvI6;R0X`wF z|Ns97GD8ntt^-nxB|(0{3=Yq3q=7g|-tI089jvk*Kn`btM`SSr1Gf+eGhVt|_XjA* zUgGKN%6^Gmn4d%Ph(nkFP>9RZ#WAE}PI3Z}&BVayv3^M*kj3EX>gTe~DWM4f=_Dpv diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/method_item.html.dist b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/method_item.html.dist deleted file mode 100644 index 1efeeb1..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/method_item.html.dist +++ /dev/null @@ -1,23 +0,0 @@ - - {name} - - - - - -
    {methods_tested_percent}{methods_tested_percent}
    - - {methods_tested_percent} - {methods_number} - {crap} - - - - - -
    {lines_executed_percent}{lines_executed_percent}
    - - {lines_executed_percent} - {num_executed_lines} / {num_executable_lines} - - diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/scarlet_red.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/scarlet_red.png deleted file mode 100644 index a879424d5a211cb33221e66174277791f59013eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryoCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#8Y?HesoB-}R}KM%WJ_ElN}Tg^b5rw57@Uhz6H8K46v{J8G895GQWe}ieFNU7 psOA9`@_4#9hHzX@u2{d4lY!ZX(W(5B_f()bgQu&X%Q~loCIDbBC29Zw diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/snow.png b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/snow.png deleted file mode 100644 index 2cdae107fceec6e7f02ac7acb4a34a82a540caa5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)ga>?NMQuI!iC1^MM!lvI6;R0X`wF|Ns97GD8ntt^-nBo-U3d c6}OTTfNUlP#;5A{K>8RwUHx3vIVCg!071?oo&W#< diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/style.css b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/style.css deleted file mode 100644 index 8c58ddc..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/style.css +++ /dev/null @@ -1,459 +0,0 @@ -/* All views: initial background and text color */ -body -{ - background-color: #fff; - color: #2e3436; - font-family: arial, helvetica, sans-serif; - font-size: 12px; - margin: 0 auto; - width: 100%; -} - -/* All views: standard link format*/ -a:link -{ - color: #2e3436; - text-decoration: underline; -} - -/* All views: standard link - visited format */ -a:visited -{ - color: #2e3436; - text-decoration: underline; -} - -/* All views: standard link - activated format */ -a:active -{ - color: #2e3436; - text-decoration: underline; -} - -/* All views: main title format */ -td.title -{ - text-align: center; - padding: 10px; - font-family: sans-serif; - font-style: italic; - font-weight: bold; - font-size: 1.6em; -} - -/* All views: header item format */ -td.headerItem -{ - text-align: right; - padding-right: 6px; - font-family: sans-serif; - font-weight: bold; -} - -/* All views: header item value format */ -td.headerValue -{ - text-align: left; - font-family: sans-serif; - font-weight: bold; -} - -/* All views: header legend item format */ -td.legendItem -{ - text-align: right; - padding-right: 6px; - padding-top: 10px; - padding-bottom: 2px; - font-family: sans-serif; - font-weight: bold; -} - -/* All views: header legend item value format */ -td.legendValue -{ - text-align: left; - padding-top: 10px; - padding-bottom: 2px; - color: #2e3436; - font-family: sans-serif; - font-weight: bold; -} - -/* All views: color of horizontal ruler */ -td.ruler -{ - background-color: #d3d7cf; -} - -/* All views: version string format */ -td.versionInfo -{ - text-align: center; - padding-top: 2px; - font-family: sans-serif; - font-style: italic; -} - -/* Directory view/File view (all)/Test case descriptions: -table headline format */ -td.tableHead -{ - text-align: center; - color: #ffffff; - background-color: #555753; - font-family: sans-serif; - font-weight: bold; -} - -/* Directory view/File view (all): filename entry format */ -td.coverItem, td.coverDirectory, td.coverFile -{ - text-align: left; - padding-left: 10px; - padding-right: 20px; - background-color: #d3d7cf; - font-family: monospace; -} - -td.coverDirectory -{ - font-weight: bold; -} - -/* Directory view/File view (all): bar-graph entry format*/ -td.coverBar -{ - padding-left: 10px; - padding-right: 10px; - background-color: #d3d7cf; -} - -/* Directory view/File view (all): bar-graph outline color */ -td.coverBarOutline -{ - background-color: #2e3436; -} - -/* Directory view/File view (all): percentage entry for files with -no coverage rate */ -td.coverPerNone -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #d3d7cf; - font-weight: bold; -} - -/* Directory view/File view (all): line count entry for files with -no coverage rate */ -td.coverNumNone -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #d3d7cf; - white-space: nowrap; -} - -/* Directory view/File view (all): percentage entry for files with -high coverage rate */ -td.coverPerHi -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #8ae234; - font-weight: bold; -} - -/* Directory view/File view (all): line count entry for files with -high coverage rate */ -td.coverNumHi -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #8ae234; - white-space: nowrap; -} - -/* Directory view/File view (all): legend entry for high coverage -rate */ -span.coverLegendHi -{ - text-align: center; - padding-left: 10px; - padding-right: 10px; - background-color: #8ae234; -} - -/* Directory view/File view (all): percentage entry for files with -medium coverage rate */ -td.coverPerMed -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #fce94f; - font-weight: bold; -} - -/* Directory view/File view (all): line count entry for files with -medium coverage rate */ -td.coverNumMed -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #fce94f; - white-space: nowrap; -} - -/* Directory view/File view (all): legend entry for medium coverage -rate */ -span.coverLegendMed -{ - text-align: center; - padding-left: 10px; - padding-right: 10px; - margin-top: 5px; - margin-bottom: 5px; - margin-right: 2px; - background-color: #fce94f; -} - -/* Directory view/File view (all): percentage entry for files with -low coverage rate */ -td.coverPerLo -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #f57900; - font-weight: bold; -} - -/* Directory view/File view (all): line count entry for files with -low coverage rate */ -td.coverNumLo -{ - text-align: right; - padding-left: 10px; - padding-right: 10px; - background-color: #f57900; - white-space: nowrap; -} - -/* Directory view/File view (all): legend entry for low coverage -rate */ -span.coverLegendLo -{ - text-align: center; - padding-left: 10px; - padding-right: 10px; - margin-right: 2px; - background-color: #f57900; -} - -/* File view (all): "show/hide details" link format */ -a.detail:link -{ - color: #ffffff; -} - -/* File view (all): "show/hide details" link - visited format */ -a.detail:visited -{ - color: #ffffff; -} - -/* File view (all): "show/hide details" link - activated format */ -a.detail:active -{ - color: #ffffff; -} - -/* File view (detail): test name table headline format */ -td.testNameHead -{ - text-align: left; - padding-left: 10px; - background-color: #729fcf; - font-family: sans-serif; - font-weight: bold; -} - -/* File view (detail): test lines table headline format */ -td.testLinesHead -{ - text-align: center; - background-color: #729fcf; - font-family: sans-serif; - font-weight: bold; -} - -/* File view (detail): test name entry */ -td.testName -{ - text-align: left; - padding-left: 10px; - background-color: #729fcf; -} - -/* File view (detail): test percentage entry */ -td.testPer -{ - text-align: right; - vertical-align: top; - padding-left: 10px; - padding-right: 10px; - background-color: #729fcf; -} - -/* File view (detail): test lines count entry */ -td.testNum -{ - text-align: right; - vertical-align: top; - padding-left: 10px; - padding-right: 10px; - background-color: #729fcf; - white-space: nowrap; -} - -/* Test case descriptions: test name format*/ -dt -{ - font-family: sans-serif; - font-weight: bold; -} - -/* Test case descriptions: description table body */ -td.testDescription -{ - padding-top: 10px; - padding-left: 30px; - padding-bottom: 10px; - padding-right: 30px; - background-color: #729fcf; -} - -/* Source code view: source code format */ -pre.source -{ - font-family: monospace; - white-space: pre; -} - -/* Source code view: line number format */ -span.lineNum -{ - background-color: #d3d7cf; -} - -span.lineNum a { - text-decoration: none; -} - -/* Source code view: format for lines which were executed */ -span.lineCov -{ - background-color: #8ae234; -} - -/* Source code view: format for Cov legend */ -span.LegendCov -{ - text-align: center; - padding-left: 10px; - padding-right: 10px; - margin-right: 2px; - background-color: #8ae234; -} - -/* Source code view: format for lines which were not executed */ -span.lineNoCov -{ - background-color: #f57900; -} - -/* Source code view: format for NoCov legend */ -span.LegendNoCov -{ - text-align: center; - padding-left: 10px; - padding-right: 10px; - margin-right: 2px; - background-color: #f57900; -} - -/* Source code view: format for lines which are dead code */ -span.lineDeadCode -{ - background-color: #d3d7cf; -} - -/* Source code view: format for NoCov legend */ -span.LegendDeadCode -{ - text-align: center; - padding-left: 10px; - padding-right: 10px; - margin-right: 2px; - background-color: #d3d7cf; -} - -/* Test view: format for tests which have passed */ -li.testPassed -{ -} - -/* Test view: format for tests which failed */ -li.testFailure -{ - background-color: #f57900; -} - -/* Test view: format for tests which failed with an error */ -li.testError -{ - background-color: #f57900; -} - -/* Test view: format for incomplete and skipped tests */ -li.testIncomplete -{ - background-color: #fcaf3e; -} - -/* CRAP */ -td.crap -{ - text-align: right; - padding-left: 10px; - padding-right: 20px; - background-color: #d3d7cf; -} - -pre span.comment { - color: #888a85; -} - -pre span.default { - color: #2e3436; -} - -pre span.html { - color: #888a85; -} - -pre span.keyword { - color: #2e3436; - font-weight: bold; -} - -pre span.string { - color: #2e3436; -} diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/yahoo-dom-event.js b/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/yahoo-dom-event.js deleted file mode 100644 index 8ab7c86..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Template/yahoo-dom-event.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright (c) 2010, Yahoo! Inc. All rights reserved. -Code licensed under the BSD License: -http://developer.yahoo.com/yui/license.html -version: 2.8.2r1 -*/ -if(typeof YAHOO=="undefined"||!YAHOO){var YAHOO={};}YAHOO.namespace=function(){var A=arguments,E=null,C,B,D;for(C=0;C0)?B.dump(I[K],N-1):Q);}else{P.push(I[K]);}P.push(O);}if(P.length>1){P.pop();}P.push("]");}else{P.push("{");for(K in I){if(B.hasOwnProperty(I,K)){P.push(K+L);if(B.isObject(I[K])){P.push((N>0)?B.dump(I[K],N-1):Q);}else{P.push(I[K]);}P.push(O);}}if(P.length>1){P.pop();}P.push("}");}return P.join("");},substitute:function(Y,J,R){var N,M,L,U,V,X,T=[],K,O="dump",S=" ",I="{",W="}",Q,P;for(;;){N=Y.lastIndexOf(I);if(N<0){break;}M=Y.indexOf(W,N);if(N+1>=M){break;}K=Y.substring(N+1,M);U=K;X=null;L=U.indexOf(S);if(L>-1){X=U.substring(L+1);U=U.substring(0,L);}V=J[U];if(R){V=R(U,V,X);}if(B.isObject(V)){if(B.isArray(V)){V=B.dump(V,parseInt(X,10));}else{X=X||"";Q=X.indexOf(O);if(Q>-1){X=X.substring(4);}P=V.toString();if(P===G||Q>-1){V=B.dump(V,parseInt(X,10));}else{V=P;}}}else{if(!B.isString(V)&&!B.isNumber(V)){V="~-"+T.length+"-~";T[T.length]=K;}}Y=Y.substring(0,N)+V+Y.substring(M+1);}for(N=T.length-1;N>=0;N=N-1){Y=Y.replace(new RegExp("~-"+N+"-~"),"{"+T[N]+"}","g");}return Y;},trim:function(I){try{return I.replace(/^\s+|\s+$/g,"");}catch(J){return I;}},merge:function(){var L={},J=arguments,I=J.length,K;for(K=0;K519)?true:false);while((G=G[u])){z[0]+=G[b];z[1]+=G[P];if(AC){z=E.Dom._calcBorders(G,z);}}if(E.Dom._getStyle(y,p)!==f){G=y;while((G=G[Z])&&G[C]){AA=G[i];AB=G[O];if(H&&(E.Dom._getStyle(G,"overflow")!=="visible")){z=E.Dom._calcBorders(G,z);}if(AA||AB){z[0]-=AB;z[1]-=AA;}}z[0]+=x;z[1]+=Y;}else{if(D){z[0]-=x;z[1]-=Y;}else{if(I||H){z[0]+=x;z[1]+=Y;}}}z[0]=Math.floor(z[0]);z[1]=Math.floor(z[1]);}else{}return z;};}}(),getX:function(G){var Y=function(x){return E.Dom.getXY(x)[0];};return E.Dom.batch(G,Y,E.Dom,true);},getY:function(G){var Y=function(x){return E.Dom.getXY(x)[1];};return E.Dom.batch(G,Y,E.Dom,true);},setXY:function(G,x,Y){E.Dom.batch(G,E.Dom._setXY,{pos:x,noRetry:Y});},_setXY:function(G,z){var AA=E.Dom._getStyle(G,p),y=E.Dom.setStyle,AD=z.pos,Y=z.noRetry,AB=[parseInt(E.Dom.getComputedStyle(G,j),10),parseInt(E.Dom.getComputedStyle(G,o),10)],AC,x;if(AA=="static"){AA=V;y(G,p,AA);}AC=E.Dom._getXY(G);if(!AD||AC===false){return false;}if(isNaN(AB[0])){AB[0]=(AA==V)?0:G[b];}if(isNaN(AB[1])){AB[1]=(AA==V)?0:G[P];}if(AD[0]!==null){y(G,j,AD[0]-AC[0]+AB[0]+"px");}if(AD[1]!==null){y(G,o,AD[1]-AC[1]+AB[1]+"px");}if(!Y){x=E.Dom._getXY(G);if((AD[0]!==null&&x[0]!=AD[0])||(AD[1]!==null&&x[1]!=AD[1])){E.Dom._setXY(G,{pos:AD,noRetry:true});}}},setX:function(Y,G){E.Dom.setXY(Y,[G,null]);},setY:function(G,Y){E.Dom.setXY(G,[null,Y]);},getRegion:function(G){var Y=function(x){var y=false;if(E.Dom._canPosition(x)){y=E.Region.getRegion(x);}else{}return y;};return E.Dom.batch(G,Y,E.Dom,true);},getClientWidth:function(){return E.Dom.getViewportWidth();},getClientHeight:function(){return E.Dom.getViewportHeight();},getElementsByClassName:function(AB,AF,AC,AE,x,AD){AF=AF||"*";AC=(AC)?E.Dom.get(AC):null||K;if(!AC){return[];}var Y=[],G=AC.getElementsByTagName(AF),z=E.Dom.hasClass;for(var y=0,AA=G.length;y-1;}}else{}return G;},addClass:function(Y,G){return E.Dom.batch(Y,E.Dom._addClass,G);},_addClass:function(x,Y){var G=false,y;if(x&&Y){y=E.Dom._getAttribute(x,F)||J;if(!E.Dom._hasClass(x,Y)){E.Dom.setAttribute(x,F,A(y+B+Y));G=true;}}else{}return G;},removeClass:function(Y,G){return E.Dom.batch(Y,E.Dom._removeClass,G);},_removeClass:function(y,x){var Y=false,AA,z,G;if(y&&x){AA=E.Dom._getAttribute(y,F)||J;E.Dom.setAttribute(y,F,AA.replace(E.Dom._getClassRegex(x),J));z=E.Dom._getAttribute(y,F);if(AA!==z){E.Dom.setAttribute(y,F,A(z));Y=true;if(E.Dom._getAttribute(y,F)===""){G=(y.hasAttribute&&y.hasAttribute(g))?g:F; -y.removeAttribute(G);}}}else{}return Y;},replaceClass:function(x,Y,G){return E.Dom.batch(x,E.Dom._replaceClass,{from:Y,to:G});},_replaceClass:function(y,x){var Y,AB,AA,G=false,z;if(y&&x){AB=x.from;AA=x.to;if(!AA){G=false;}else{if(!AB){G=E.Dom._addClass(y,x.to);}else{if(AB!==AA){z=E.Dom._getAttribute(y,F)||J;Y=(B+z.replace(E.Dom._getClassRegex(AB),B+AA)).split(E.Dom._getClassRegex(AA));Y.splice(1,0,B+AA);E.Dom.setAttribute(y,F,A(Y.join(J)));G=true;}}}}else{}return G;},generateId:function(G,x){x=x||"yui-gen";var Y=function(y){if(y&&y.id){return y.id;}var z=x+YAHOO.env._id_counter++;if(y){if(y[e]&&y[e].getElementById(z)){return E.Dom.generateId(y,z+x);}y.id=z;}return z;};return E.Dom.batch(G,Y,E.Dom,true)||Y.apply(E.Dom,arguments);},isAncestor:function(Y,x){Y=E.Dom.get(Y);x=E.Dom.get(x);var G=false;if((Y&&x)&&(Y[l]&&x[l])){if(Y.contains&&Y!==x){G=Y.contains(x);}else{if(Y.compareDocumentPosition){G=!!(Y.compareDocumentPosition(x)&16);}}}else{}return G;},inDocument:function(G,Y){return E.Dom._inDoc(E.Dom.get(G),Y);},_inDoc:function(Y,x){var G=false;if(Y&&Y[C]){x=x||Y[e];G=E.Dom.isAncestor(x[v],Y);}else{}return G;},getElementsBy:function(Y,AF,AB,AD,y,AC,AE){AF=AF||"*";AB=(AB)?E.Dom.get(AB):null||K;if(!AB){return[];}var x=[],G=AB.getElementsByTagName(AF);for(var z=0,AA=G.length;z=8&&K.documentElement.hasAttribute){E.Dom.DOT_ATTRIBUTES.type=true;}})();YAHOO.util.Region=function(C,D,A,B){this.top=C;this.y=C;this[1]=C;this.right=D;this.bottom=A;this.left=B;this.x=B;this[0]=B; -this.width=this.right-this.left;this.height=this.bottom-this.top;};YAHOO.util.Region.prototype.contains=function(A){return(A.left>=this.left&&A.right<=this.right&&A.top>=this.top&&A.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(E){var C=Math.max(this.top,E.top),D=Math.min(this.right,E.right),A=Math.min(this.bottom,E.bottom),B=Math.max(this.left,E.left);if(A>=C&&D>=B){return new YAHOO.util.Region(C,D,A,B);}else{return null;}};YAHOO.util.Region.prototype.union=function(E){var C=Math.min(this.top,E.top),D=Math.max(this.right,E.right),A=Math.max(this.bottom,E.bottom),B=Math.min(this.left,E.left);return new YAHOO.util.Region(C,D,A,B);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+", height: "+this.height+", width: "+this.width+"}");};YAHOO.util.Region.getRegion=function(D){var F=YAHOO.util.Dom.getXY(D),C=F[1],E=F[0]+D.offsetWidth,A=F[1]+D.offsetHeight,B=F[0];return new YAHOO.util.Region(C,E,A,B);};YAHOO.util.Point=function(A,B){if(YAHOO.lang.isArray(A)){B=A[1];A=A[0];}YAHOO.util.Point.superclass.constructor.call(this,B,A,B,A);};YAHOO.extend(YAHOO.util.Point,YAHOO.util.Region);(function(){var B=YAHOO.util,A="clientTop",F="clientLeft",J="parentNode",K="right",W="hasLayout",I="px",U="opacity",L="auto",D="borderLeftWidth",G="borderTopWidth",P="borderRightWidth",V="borderBottomWidth",S="visible",Q="transparent",N="height",E="width",H="style",T="currentStyle",R=/^width|height$/,O=/^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,M={get:function(X,Z){var Y="",a=X[T][Z];if(Z===U){Y=B.Dom.getStyle(X,U);}else{if(!a||(a.indexOf&&a.indexOf(I)>-1)){Y=a;}else{if(B.Dom.IE_COMPUTED[Z]){Y=B.Dom.IE_COMPUTED[Z](X,Z);}else{if(O.test(a)){Y=B.Dom.IE.ComputedStyle.getPixel(X,Z);}else{Y=a;}}}}return Y;},getOffset:function(Z,e){var b=Z[T][e],X=e.charAt(0).toUpperCase()+e.substr(1),c="offset"+X,Y="pixel"+X,a="",d;if(b==L){d=Z[c];if(d===undefined){a=0;}a=d;if(R.test(e)){Z[H][e]=d;if(Z[c]>d){a=d-(Z[c]-d);}Z[H][e]=L;}}else{if(!Z[H][Y]&&!Z[H][e]){Z[H][e]=b;}a=Z[H][Y];}return a+I;},getBorderWidth:function(X,Z){var Y=null;if(!X[T][W]){X[H].zoom=1;}switch(Z){case G:Y=X[A];break;case V:Y=X.offsetHeight-X.clientHeight-X[A];break;case D:Y=X[F];break;case P:Y=X.offsetWidth-X.clientWidth-X[F];break;}return Y+I;},getPixel:function(Y,X){var a=null,b=Y[T][K],Z=Y[T][X];Y[H][K]=Z;a=Y[H].pixelRight;Y[H][K]=b;return a+I;},getMargin:function(Y,X){var Z;if(Y[T][X]==L){Z=0+I;}else{Z=B.Dom.IE.ComputedStyle.getPixel(Y,X);}return Z;},getVisibility:function(Y,X){var Z;while((Z=Y[T])&&Z[X]=="inherit"){Y=Y[J];}return(Z)?Z[X]:S;},getColor:function(Y,X){return B.Dom.Color.toRGB(Y[T][X])||Q;},getBorderColor:function(Y,X){var Z=Y[T],a=Z[X]||Z.color;return B.Dom.Color.toRGB(B.Dom.Color.toHex(a));}},C={};C.top=C.right=C.bottom=C.left=C[E]=C[N]=M.getOffset;C.color=M.getColor;C[G]=C[P]=C[V]=C[D]=M.getBorderWidth;C.marginTop=C.marginRight=C.marginBottom=C.marginLeft=M.getMargin;C.visibility=M.getVisibility;C.borderColor=C.borderTopColor=C.borderRightColor=C.borderBottomColor=C.borderLeftColor=M.getBorderColor;B.Dom.IE_COMPUTED=C;B.Dom.IE_ComputedStyle=M;})();(function(){var C="toString",A=parseInt,B=RegExp,D=YAHOO.util;D.Dom.Color={KEYWORDS:{black:"000",silver:"c0c0c0",gray:"808080",white:"fff",maroon:"800000",red:"f00",purple:"800080",fuchsia:"f0f",green:"008000",lime:"0f0",olive:"808000",yellow:"ff0",navy:"000080",blue:"00f",teal:"008080",aqua:"0ff"},re_RGB:/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(E){if(!D.Dom.Color.re_RGB.test(E)){E=D.Dom.Color.toHex(E);}if(D.Dom.Color.re_hex.exec(E)){E="rgb("+[A(B.$1,16),A(B.$2,16),A(B.$3,16)].join(", ")+")";}return E;},toHex:function(H){H=D.Dom.Color.KEYWORDS[H]||H;if(D.Dom.Color.re_RGB.exec(H)){var G=(B.$1.length===1)?"0"+B.$1:Number(B.$1),F=(B.$2.length===1)?"0"+B.$2:Number(B.$2),E=(B.$3.length===1)?"0"+B.$3:Number(B.$3);H=[G[C](16),F[C](16),E[C](16)].join("");}if(H.length<6){H=H.replace(D.Dom.Color.re_hex3,"$1$1");}if(H!=="transparent"&&H.indexOf("#")<0){H="#"+H;}return H.toLowerCase();}};}());YAHOO.register("dom",YAHOO.util.Dom,{version:"2.8.2r1",build:"7"});YAHOO.util.CustomEvent=function(D,C,B,A,E){this.type=D;this.scope=C||window;this.silent=B;this.fireOnce=E;this.fired=false;this.firedWith=null;this.signature=A||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var F="_YUICEOnSubscribe";if(D!==F){this.subscribeEvent=new YAHOO.util.CustomEvent(F,this,true);}this.lastError=null;};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(B,C,D){if(!B){throw new Error("Invalid callback for subscriber to '"+this.type+"'");}if(this.subscribeEvent){this.subscribeEvent.fire(B,C,D);}var A=new YAHOO.util.Subscriber(B,C,D);if(this.fireOnce&&this.fired){this.notify(A,this.firedWith);}else{this.subscribers.push(A);}},unsubscribe:function(D,F){if(!D){return this.unsubscribeAll();}var E=false;for(var B=0,A=this.subscribers.length;B0){H=C[0];}try{B=F.fn.call(E,H,F.obj);}catch(G){this.lastError=G;if(A){throw G;}}}else{try{B=F.fn.call(E,this.type,C,F.obj);}catch(D){this.lastError=D;if(A){throw D;}}}return B;},unsubscribeAll:function(){var A=this.subscribers.length,B;for(B=A-1;B>-1;B--){this._delete(B);}this.subscribers=[];return A;},_delete:function(A){var B=this.subscribers[A];if(B){delete B.fn;delete B.obj;}this.subscribers.splice(A,1);},toString:function(){return"CustomEvent: "+"'"+this.type+"', "+"context: "+this.scope;}};YAHOO.util.Subscriber=function(A,B,C){this.fn=A;this.obj=YAHOO.lang.isUndefined(B)?null:B;this.overrideContext=C;};YAHOO.util.Subscriber.prototype.getScope=function(A){if(this.overrideContext){if(this.overrideContext===true){return this.obj;}else{return this.overrideContext;}}return A;};YAHOO.util.Subscriber.prototype.contains=function(A,B){if(B){return(this.fn==A&&this.obj==B);}else{return(this.fn==A);}};YAHOO.util.Subscriber.prototype.toString=function(){return"Subscriber { obj: "+this.obj+", overrideContext: "+(this.overrideContext||"no")+" }";};if(!YAHOO.util.Event){YAHOO.util.Event=function(){var G=false,H=[],J=[],A=0,E=[],B=0,C={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9},D=YAHOO.env.ua.ie,F="focusin",I="focusout";return{POLL_RETRYS:500,POLL_INTERVAL:40,EL:0,TYPE:1,FN:2,WFN:3,UNLOAD_OBJ:3,ADJ_SCOPE:4,OBJ:5,OVERRIDE:6,CAPTURE:7,lastError:null,isSafari:YAHOO.env.ua.webkit,webkit:YAHOO.env.ua.webkit,isIE:D,_interval:null,_dri:null,_specialTypes:{focusin:(D?"focusin":"focus"),focusout:(D?"focusout":"blur")},DOMReady:false,throwErrors:false,startInterval:function(){if(!this._interval){this._interval=YAHOO.lang.later(this.POLL_INTERVAL,this,this._tryPreloadAttach,null,true);}},onAvailable:function(Q,M,O,P,N){var K=(YAHOO.lang.isString(Q))?[Q]:Q;for(var L=0;L-1;M--){S=(this.removeListener(L[M],K,R)&&S);}return S;}}if(!R||!R.call){return this.purgeElement(L,false,K);}if("unload"==K){for(M=J.length-1;M>-1;M--){U=J[M];if(U&&U[0]==L&&U[1]==K&&U[2]==R){J.splice(M,1);return true;}}return false;}var N=null;var O=arguments[3];if("undefined"===typeof O){O=this._getCacheIndex(H,L,K,R);}if(O>=0){N=H[O];}if(!L||!N){return false;}var T=N[this.CAPTURE]===true?true:false;try{this._simpleRemove(L,K,N[this.WFN],T);}catch(Q){this.lastError=Q;return false;}delete H[O][this.WFN];delete H[O][this.FN];H.splice(O,1);return true;},getTarget:function(M,L){var K=M.target||M.srcElement;return this.resolveTextNode(K);},resolveTextNode:function(L){try{if(L&&3==L.nodeType){return L.parentNode;}}catch(K){}return L;},getPageX:function(L){var K=L.pageX;if(!K&&0!==K){K=L.clientX||0;if(this.isIE){K+=this._getScrollLeft();}}return K;},getPageY:function(K){var L=K.pageY;if(!L&&0!==L){L=K.clientY||0;if(this.isIE){L+=this._getScrollTop();}}return L;},getXY:function(K){return[this.getPageX(K),this.getPageY(K)];},getRelatedTarget:function(L){var K=L.relatedTarget;if(!K){if(L.type=="mouseout"){K=L.toElement; -}else{if(L.type=="mouseover"){K=L.fromElement;}}}return this.resolveTextNode(K);},getTime:function(M){if(!M.time){var L=new Date().getTime();try{M.time=L;}catch(K){this.lastError=K;return L;}}return M.time;},stopEvent:function(K){this.stopPropagation(K);this.preventDefault(K);},stopPropagation:function(K){if(K.stopPropagation){K.stopPropagation();}else{K.cancelBubble=true;}},preventDefault:function(K){if(K.preventDefault){K.preventDefault();}else{K.returnValue=false;}},getEvent:function(M,K){var L=M||window.event;if(!L){var N=this.getEvent.caller;while(N){L=N.arguments[0];if(L&&Event==L.constructor){break;}N=N.caller;}}return L;},getCharCode:function(L){var K=L.keyCode||L.charCode||0;if(YAHOO.env.ua.webkit&&(K in C)){K=C[K];}return K;},_getCacheIndex:function(M,P,Q,O){for(var N=0,L=M.length;N0&&E.length>0);}var P=[];var R=function(T,U){var S=T;if(U.overrideContext){if(U.overrideContext===true){S=U.obj;}else{S=U.overrideContext;}}U.fn.call(S,U.obj);};var L,K,O,N,M=[];for(L=0,K=E.length;L-1;L--){O=E[L];if(!O||!O.id){E.splice(L,1);}}this.startInterval();}else{if(this._interval){this._interval.cancel();this._interval=null;}}this.locked=false;},purgeElement:function(O,P,R){var M=(YAHOO.lang.isString(O))?this.getEl(O):O;var Q=this.getListeners(M,R),N,K;if(Q){for(N=Q.length-1;N>-1;N--){var L=Q[N];this.removeListener(M,L.type,L.fn);}}if(P&&M&&M.childNodes){for(N=0,K=M.childNodes.length;N-1;N--){M=H[N];if(M){L.removeListener(M[L.EL],M[L.TYPE],M[L.FN],N);}}M=null;}L._simpleRemove(window,"unload",L._unload);},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var K=document.documentElement,L=document.body;if(K&&(K.scrollTop||K.scrollLeft)){return[K.scrollTop,K.scrollLeft];}else{if(L){return[L.scrollTop,L.scrollLeft];}else{return[0,0];}}},regCE:function(){},_simpleAdd:function(){if(window.addEventListener){return function(M,N,L,K){M.addEventListener(N,L,(K));};}else{if(window.attachEvent){return function(M,N,L,K){M.attachEvent("on"+N,L);};}else{return function(){};}}}(),_simpleRemove:function(){if(window.removeEventListener){return function(M,N,L,K){M.removeEventListener(N,L,(K));};}else{if(window.detachEvent){return function(L,M,K){L.detachEvent("on"+M,K);};}else{return function(){};}}}()};}();(function(){var EU=YAHOO.util.Event;EU.on=EU.addListener;EU.onFocus=EU.addFocusListener;EU.onBlur=EU.addBlurListener; -/* DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */ -if(EU.isIE){if(self!==self.top){document.onreadystatechange=function(){if(document.readyState=="complete"){document.onreadystatechange=null;EU._ready();}};}else{YAHOO.util.Event.onDOMReady(YAHOO.util.Event._tryPreloadAttach,YAHOO.util.Event,true);var n=document.createElement("p");EU._dri=setInterval(function(){try{n.doScroll("left");clearInterval(EU._dri);EU._dri=null;EU._ready();n=null;}catch(ex){}},EU.POLL_INTERVAL);}}else{if(EU.webkit&&EU.webkit<525){EU._dri=setInterval(function(){var rs=document.readyState;if("loaded"==rs||"complete"==rs){clearInterval(EU._dri);EU._dri=null;EU._ready();}},EU.POLL_INTERVAL);}else{EU._simpleAdd(document,"DOMContentLoaded",EU._ready);}}EU._simpleAdd(window,"load",EU._load);EU._simpleAdd(window,"unload",EU._unload);EU._tryPreloadAttach();})();}YAHOO.util.EventProvider=function(){};YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(A,C,F,E){this.__yui_events=this.__yui_events||{};var D=this.__yui_events[A];if(D){D.subscribe(C,F,E);}else{this.__yui_subscribers=this.__yui_subscribers||{};var B=this.__yui_subscribers;if(!B[A]){B[A]=[];}B[A].push({fn:C,obj:F,overrideContext:E});}},unsubscribe:function(C,E,G){this.__yui_events=this.__yui_events||{};var A=this.__yui_events;if(C){var F=A[C];if(F){return F.unsubscribe(E,G);}}else{var B=true;for(var D in A){if(YAHOO.lang.hasOwnProperty(A,D)){B=B&&A[D].unsubscribe(E,G);}}return B;}return false;},unsubscribeAll:function(A){return this.unsubscribe(A); -},createEvent:function(B,G){this.__yui_events=this.__yui_events||{};var E=G||{},D=this.__yui_events,F;if(D[B]){}else{F=new YAHOO.util.CustomEvent(B,E.scope||this,E.silent,YAHOO.util.CustomEvent.FLAT,E.fireOnce);D[B]=F;if(E.onSubscribeCallback){F.subscribeEvent.subscribe(E.onSubscribeCallback);}this.__yui_subscribers=this.__yui_subscribers||{};var A=this.__yui_subscribers[B];if(A){for(var C=0;C{tests}", - "footer": "" - }, diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/Node.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Node.php new file mode 100644 index 0000000..481e309 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/Directory.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Node/Directory.php similarity index 57% rename from libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/Directory.php rename to libs/PHPUnit/PHP/CodeCoverage/Report/Node/Directory.php index a9af0ff..808e4aa 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/Directory.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Report/Node/Directory.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,40 +37,37 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 + * @since File available since Release 1.1.0 */ -require_once 'PHP/CodeCoverage/Report/HTML/Node/Iterator.php'; - /** * Represents a directory in the code coverage information tree. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 + * @since Class available since Release 1.1.0 */ -class PHP_CodeCoverage_Report_HTML_Node_Directory extends PHP_CodeCoverage_Report_HTML_Node implements IteratorAggregate +class PHP_CodeCoverage_Report_Node_Directory extends PHP_CodeCoverage_Report_Node implements IteratorAggregate { /** - * @var PHP_CodeCoverage_Report_HTML_Node[] + * @var PHP_CodeCoverage_Report_Node[] */ protected $children = array(); /** - * @var PHP_CodeCoverage_Report_HTML_Node_Directory[] + * @var PHP_CodeCoverage_Report_Node_Directory[] */ protected $directories = array(); /** - * @var PHP_CodeCoverage_Report_HTML_Node_File[] + * @var PHP_CodeCoverage_Report_Node_File[] */ protected $files = array(); @@ -79,6 +76,26 @@ class PHP_CodeCoverage_Report_HTML_Node_Directory extends PHP_CodeCoverage_Repor */ protected $classes; + /** + * @var array + */ + protected $traits; + + /** + * @var array + */ + protected $functions; + + /** + * @var array + */ + protected $linesOfCode = NULL; + + /** + * @var integer + */ + protected $numFiles = -1; + /** * @var integer */ @@ -99,6 +116,16 @@ class PHP_CodeCoverage_Report_HTML_Node_Directory extends PHP_CodeCoverage_Repor */ protected $numTestedClasses = -1; + /** + * @var integer + */ + protected $numTraits = -1; + + /** + * @var integer + */ + protected $numTestedTraits = -1; + /** * @var integer */ @@ -109,6 +136,34 @@ class PHP_CodeCoverage_Report_HTML_Node_Directory extends PHP_CodeCoverage_Repor */ 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. * @@ -117,7 +172,7 @@ class PHP_CodeCoverage_Report_HTML_Node_Directory extends PHP_CodeCoverage_Repor public function getIterator() { return new RecursiveIteratorIterator( - new PHP_CodeCoverage_Report_HTML_Node_Iterator($this), + new PHP_CodeCoverage_Report_Node_Iterator($this), RecursiveIteratorIterator::SELF_FIRST ); } @@ -125,13 +180,12 @@ public function getIterator() /** * Adds a new directory. * - * @return PHP_CodeCoverage_Report_HTML_Node_Directory + * @param string $name + * @return PHP_CodeCoverage_Report_Node_Directory */ public function addDirectory($name) { - $directory = new PHP_CodeCoverage_Report_HTML_Node_Directory( - $name, $this - ); + $directory = new PHP_CodeCoverage_Report_Node_Directory($name, $this); $this->children[] = $directory; $this->directories[] = &$this->children[count($this->children) - 1]; @@ -143,16 +197,16 @@ public function addDirectory($name) * Adds a new file. * * @param string $name - * @param array $lines - * @param boolean $yui - * @param boolean $highlight - * @return PHP_CodeCoverage_Report_HTML_Node_File - * @throws RuntimeException + * @param array $coverageData + * @param array $testData + * @param boolean $cacheTokens + * @return PHP_CodeCoverage_Report_Node_File + * @throws PHP_CodeCoverage_Exception */ - public function addFile($name, array $lines, $yui, $highlight) + public function addFile($name, array $coverageData, array $testData, $cacheTokens) { - $file = new PHP_CodeCoverage_Report_HTML_Node_File( - $name, $this, $lines, $yui, $highlight + $file = new PHP_CodeCoverage_Report_Node_File( + $name, $this, $coverageData, $testData, $cacheTokens ); $this->children[] = $file; @@ -214,6 +268,68 @@ public function 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. * @@ -287,144 +403,110 @@ public function getNumTestedClasses() } /** - * Returns the number of methods. + * Returns the number of traits. * * @return integer */ - public function getNumMethods() + public function getNumTraits() { - if ($this->numMethods == -1) { - $this->numMethods = 0; + if ($this->numTraits == -1) { + $this->numTraits = 0; foreach ($this->children as $child) { - $this->numMethods += $child->getNumMethods(); + $this->numTraits += $child->getNumTraits(); } } - return $this->numMethods; + return $this->numTraits; } /** - * Returns the number of tested methods. + * Returns the number of tested traits. * * @return integer */ - public function getNumTestedMethods() + public function getNumTestedTraits() { - if ($this->numTestedMethods == -1) { - $this->numTestedMethods = 0; + if ($this->numTestedTraits == -1) { + $this->numTestedTraits = 0; foreach ($this->children as $child) { - $this->numTestedMethods += $child->getNumTestedMethods(); + $this->numTestedTraits += $child->getNumTestedTraits(); } } - return $this->numTestedMethods; + return $this->numTestedTraits; } /** - * Renders this node. + * Returns the number of methods. * - * @param string $target - * @param string $title - * @param string $charset - * @param integer $lowUpperBound - * @param integer $highLowerBound - * @param string $generator - */ - public function render($target, $title, $charset = 'UTF-8', $lowUpperBound = 35, $highLowerBound = 70, $generator = '') + * @return integer + */ + public function getNumMethods() { - $this->doRender( - $target, $title, $charset, $lowUpperBound, $highLowerBound, $generator - ); + if ($this->numMethods == -1) { + $this->numMethods = 0; - foreach ($this->children as $child) { - $child->render( - $target, - $title, - $charset, - $lowUpperBound, - $highLowerBound, - $generator - ); + foreach ($this->children as $child) { + $this->numMethods += $child->getNumMethods(); + } } - $this->children = array(); + return $this->numMethods; } /** - * @param string $target - * @param string $title - * @param string $charset - * @param integer $lowUpperBound - * @param integer $highLowerBound - * @param string $generator + * Returns the number of tested methods. + * + * @return integer */ - protected function doRender($target, $title, $charset, $lowUpperBound, $highLowerBound, $generator) + public function getNumTestedMethods() { - $cleanId = PHP_CodeCoverage_Util::getSafeFilename($this->getId()); - $file = $target . $cleanId . '.html'; - - $template = new Text_Template( - PHP_CodeCoverage_Report_HTML::$templatePath . 'directory.html' - ); - - $this->setTemplateVars($template, $title, $charset, $generator); - - $template->setVar( - array( - 'total_item' => $this->renderTotalItem( - $lowUpperBound, $highLowerBound - ), - 'items' => $this->renderItems( - $lowUpperBound, $highLowerBound - ), - 'low_upper_bound' => $lowUpperBound, - 'high_lower_bound' => $highLowerBound - ) - ); + if ($this->numTestedMethods == -1) { + $this->numTestedMethods = 0; - $template->renderTo($file); + foreach ($this->children as $child) { + $this->numTestedMethods += $child->getNumTestedMethods(); + } + } - $this->directories = array(); - $this->files = array(); + return $this->numTestedMethods; } /** - * @param float $lowUpperBound - * @param float $highLowerBound - * @return string + * Returns the number of functions. + * + * @return integer */ - protected function renderItems($lowUpperBound, $highLowerBound) + public function getNumFunctions() { - $items = $this->doRenderItems( - $this->directories, $lowUpperBound, $highLowerBound, 'coverDirectory' - ); + if ($this->numFunctions == -1) { + $this->numFunctions = 0; - $items .= $this->doRenderItems( - $this->files, $lowUpperBound, $highLowerBound, 'coverFile' - ); + foreach ($this->children as $child) { + $this->numFunctions += $child->getNumFunctions(); + } + } - return $items; + return $this->numFunctions; } /** - * @param array $items - * @param float $lowUpperBound - * @param float $highLowerBound - * @param string $itemClass - * @return string + * Returns the number of tested functions. + * + * @return integer */ - protected function doRenderItems(array $items, $lowUpperBound, $highLowerBound, $itemClass) + public function getNumTestedFunctions() { - $result = ''; + if ($this->numTestedFunctions == -1) { + $this->numTestedFunctions = 0; - foreach ($items as $item) { - $result .= $this->doRenderItemObject( - $item, $lowUpperBound, $highLowerBound, NULL, $itemClass - ); + foreach ($this->children as $child) { + $this->numTestedFunctions += $child->getNumTestedFunctions(); + } } - return $result; + return $this->numTestedFunctions; } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/Node/File.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Node/File.php new file mode 100644 index 0000000..a5b0154 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/Iterator.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Node/Iterator.php similarity index 77% rename from libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/Iterator.php rename to libs/PHPUnit/PHP/CodeCoverage/Report/Node/Iterator.php index e49caa3..6c8dd05 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Report/HTML/Node/Iterator.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Report/Node/Iterator.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,25 +37,24 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 + * @since File available since Release 1.1.0 */ /** - * Recursive iterator for PHP_CodeCoverage_Report_HTML_Node object graphs. + * Recursive iterator for PHP_CodeCoverage_Report_Node object graphs. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 + * @since Class available since Release 1.1.0 */ -class PHP_CodeCoverage_Report_HTML_Node_Iterator implements RecursiveIterator +class PHP_CodeCoverage_Report_Node_Iterator implements RecursiveIterator { /** * @var integer @@ -63,16 +62,16 @@ class PHP_CodeCoverage_Report_HTML_Node_Iterator implements RecursiveIterator protected $position; /** - * @var PHP_CodeCoverage_Report_HTML_Node[] + * @var PHP_CodeCoverage_Report_Node[] */ protected $nodes; /** * Constructor. * - * @param PHP_CodeCoverage_Report_HTML_Node_Directory $node + * @param PHP_CodeCoverage_Report_Node_Directory $node */ - public function __construct(PHP_CodeCoverage_Report_HTML_Node_Directory $node) + public function __construct(PHP_CodeCoverage_Report_Node_Directory $node) { $this->nodes = $node->getChildNodes(); } @@ -128,11 +127,11 @@ public function next() /** * Returns the sub iterator for the current element. * - * @return PHP_CodeCoverage_Report_HTML_Node_Iterator + * @return PHP_CodeCoverage_Report_Node_Iterator */ public function getChildren() { - return new PHP_CodeCoverage_Report_HTML_Node_Iterator( + return new PHP_CodeCoverage_Report_Node_Iterator( $this->nodes[$this->position] ); } @@ -144,6 +143,6 @@ public function getChildren() */ public function hasChildren() { - return $this->nodes[$this->position] instanceof PHP_CodeCoverage_Report_HTML_Node_Directory; + return $this->nodes[$this->position] instanceof PHP_CodeCoverage_Report_Node_Directory; } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Report/PHP.php b/libs/PHPUnit/PHP/CodeCoverage/Report/PHP.php new file mode 100644 index 0000000..5d17e86 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Report/Text.php b/libs/PHPUnit/PHP/CodeCoverage/Report/Text.php new file mode 100644 index 0000000..5eecb09 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/TextUI/Command.php b/libs/PHPUnit/PHP/CodeCoverage/TextUI/Command.php deleted file mode 100644 index 9f667f1..0000000 --- a/libs/PHPUnit/PHP/CodeCoverage/TextUI/Command.php +++ /dev/null @@ -1,268 +0,0 @@ -. - * 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-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since File available since Release 1.0.0 - */ - -require_once 'PHP/CodeCoverage.php'; - -require_once 'ezc/Base/base.php'; -spl_autoload_register(array('ezcBase', 'autoload')); - -/** - * TextUI frontend for PHP_CodeCoverage. - * - * @category PHP - * @package CodeCoverage - * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 - * @link http://github.com/sebastianbergmann/php-code-coverage - * @since Class available since Release 1.0.0 - */ -class PHP_CodeCoverage_TextUI_Command -{ - /** - * Main method. - */ - public static function main() - { - $input = new ezcConsoleInput; - - $input->registerOption( - new ezcConsoleOption( - '', - 'clover', - ezcConsoleInput::TYPE_STRING - ) - ); - - $input->registerOption( - new ezcConsoleOption( - '', - 'html', - ezcConsoleInput::TYPE_STRING - ) - ); - - $input->registerOption( - new ezcConsoleOption( - '', - 'blacklist', - ezcConsoleInput::TYPE_STRING, - array(), - TRUE - ) - ); - - $input->registerOption( - new ezcConsoleOption( - '', - 'whitelist', - ezcConsoleInput::TYPE_STRING, - array(), - TRUE - ) - ); - - $input->registerOption( - new ezcConsoleOption( - 'h', - 'help', - ezcConsoleInput::TYPE_NONE, - NULL, - FALSE, - '', - '', - array(), - array(), - FALSE, - FALSE, - TRUE - ) - ); - - $input->registerOption( - new ezcConsoleOption( - 'v', - 'version', - ezcConsoleInput::TYPE_NONE, - NULL, - FALSE, - '', - '', - array(), - array(), - FALSE, - FALSE, - TRUE - ) - ); - - try { - $input->process(); - } - - catch (ezcConsoleOptionException $e) { - print $e->getMessage() . "\n"; - exit(1); - } - - if ($input->getOption('help')->value) { - self::showHelp(); - exit(0); - } - - else if ($input->getOption('version')->value) { - self::printVersionString(); - exit(0); - } - - $arguments = $input->getArguments(); - $clover = $input->getOption('clover')->value; - $html = $input->getOption('html')->value; - $blacklist = $input->getOption('blacklist')->value; - $whitelist = $input->getOption('whitelist')->value; - - if (count($arguments) == 1) { - self::printVersionString(); - - $coverage = new PHP_CodeCoverage; - $filter = $coverage->filter(); - - if (empty($whitelist)) { - $c = new ReflectionClass('ezcBase'); - $filter->addDirectoryToBlacklist(dirname($c->getFileName())); - $c = new ReflectionClass('ezcConsoleInput'); - $filter->addDirectoryToBlacklist(dirname($c->getFileName())); - - foreach ($blacklist as $item) { - if (is_dir($item)) { - $filter->addDirectoryToBlacklist($item); - } - - else if (is_file($item)) { - $filter->addFileToBlacklist($item); - } - } - } else { - foreach ($whitelist as $item) { - if (is_dir($item)) { - $filter->addDirectoryToWhitelist($item); - } - - else if (is_file($item)) { - $filter->addFileToWhitelist($item); - } - } - } - - $coverage->start('phpcov'); - - require $arguments[0]; - - $coverage->stop(); - - if ($clover) { - require 'PHP/CodeCoverage/Report/Clover.php'; - - $writer = new PHP_CodeCoverage_Report_Clover; - $writer->process($coverage, $clover); - } - - if ($html) { - require 'PHP/CodeCoverage/Report/HTML.php'; - - $writer = new PHP_CodeCoverage_Report_HTML; - $writer->process($coverage, $html); - } - } else { - self::showHelp(); - exit(1); - } - } - - /** - * Shows an error. - * - * @param string $message - */ - protected static function showError($message) - { - self::printVersionString(); - - print $message; - - exit(1); - } - - /** - * Shows the help. - */ - protected static function showHelp() - { - self::printVersionString(); - - print << - - --clover Write code coverage data in Clover XML format. - --html Generate code coverage report in HTML format. - - --blacklist Adds to the blacklist. - --whitelist Adds to the whitelist. - - --help Prints this usage information. - --version Prints the version and exits. - -EOT; - } - - /** - * Prints the version string. - */ - protected static function printVersionString() - { - print "phpcov 1.0.4 by Sebastian Bergmann.\n\n"; - } -} diff --git a/libs/PHPUnit/PHP/CodeCoverage/Util.php b/libs/PHPUnit/PHP/CodeCoverage/Util.php index 80d7192..60f0491 100644 --- a/libs/PHPUnit/PHP/CodeCoverage/Util.php +++ b/libs/PHPUnit/PHP/CodeCoverage/Util.php @@ -2,7 +2,7 @@ /** * PHP_CodeCoverage * - * Copyright (c) 2009-2011, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,37 +37,25 @@ * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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('T_NAMESPACE')) { - define('T_NAMESPACE', 377); -} - -require_once 'PHP/Token/Stream/CachingFactory.php'; - /** * Utility methods. * * @category PHP * @package CodeCoverage * @author Sebastian Bergmann - * @copyright 2009-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.4 + * @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 string - */ - const REGEX = '(@covers\s+(?P.*?)\s*$)m'; - /** * @var array */ @@ -76,220 +64,152 @@ class PHP_CodeCoverage_Util /** * @var array */ - protected static $templateMethods = array( - 'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown' - ); - - /** - * 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 - */ - public static 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 = ''; - } + protected static $ids = array(); - $pointer = &$pointer[$path[$i] . $type]; - } - - $pointer = $file; - } - - return $result; - } /** - * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code - * based on its cyclomatic complexity and percentage of code coverage. + * Returns the lines of a source file that should be ignored. * - * @param integer $ccn - * @param float $coverage - * @return string - */ - public static 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 - ); - } - - /** - * @param string $directory - * @return string - * @throws RuntimeException + * @param string $filename + * @param boolean $cacheTokens + * @return array + * @throws PHP_CodeCoverage_Exception */ - public static function getDirectory($directory) + public static function getLinesToBeIgnored($filename, $cacheTokens = TRUE) { - if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { - $directory .= DIRECTORY_SEPARATOR; - } - - if (is_dir($directory)) { - return $directory; + if (!is_string($filename)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 1, 'string' + ); } - if (mkdir($directory, 0777, TRUE)) { - return $directory; + if (!is_bool($cacheTokens)) { + throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( + 2, 'boolean' + ); } - throw new RuntimeException( - sprintf( - 'Directory "%s" does not exist.', - $directory - ) - ); - } + if (!isset(self::$ignoredLines[$filename])) { + self::$ignoredLines[$filename] = array(); + $ignore = FALSE; + $stop = FALSE; + $lines = file($filename); - /** - * Returns the files and lines a test method wants to cover. - * - * @param string $className - * @param string $methodName - * @return array - */ - public static function getLinesToBeCovered($className, $methodName) - { - $codeToCoverList = array(); - $result = array(); - // @codeCoverageIgnoreStart - if (($pos = strpos($methodName, ' ')) !== FALSE) { - $methodName = substr($methodName, 0, $pos); - } - // @codeCoverageIgnoreEnd - $class = new ReflectionClass($className); - $method = new ReflectionMethod($className, $methodName); - $docComment = $class->getDocComment() . $method->getDocComment(); - - foreach (self::$templateMethods as $templateMethod) { - if ($class->hasMethod($templateMethod)) { - $reflector = $class->getMethod($templateMethod); - $docComment .= $reflector->getDocComment(); - unset($reflector); + foreach ($lines as $index => $line) { + if (!trim($line)) { + self::$ignoredLines[$filename][$index+1] = TRUE; + } } - } - if (preg_match_all(self::REGEX, $docComment, $matches)) { - foreach ($matches['coveredElement'] as $coveredElement) { - $codeToCoverList = array_merge( - $codeToCoverList, - self::resolveCoversToReflectionObjects($coveredElement) - ); + if ($cacheTokens) { + $tokens = PHP_Token_Stream_CachingFactory::get($filename); + } else { + $tokens = new PHP_Token_Stream($filename); } - foreach ($codeToCoverList as $codeToCover) { - $fileName = $codeToCover->getFileName(); + $classes = array_merge($tokens->getClasses(), $tokens->getTraits()); + $tokens = $tokens->tokens(); - if (!isset($result[$fileName])) { - $result[$fileName] = array(); - } - - $result[$fileName] = array_unique( - array_merge( - $result[$fileName], - range( - $codeToCover->getStartLine(), $codeToCover->getEndLine() - ) - ) - ); - } - } - - return $result; - } + foreach ($tokens as $token) { + switch (get_class($token)) { + case 'PHP_Token_DOC_COMMENT': { + $count = substr_count($token, "\n"); + $line = $token->getLine(); - /** - * Returns the lines of a source file that should be ignored. - * - * @param string $filename - * @return array - */ - public static function getLinesToBeIgnored($filename) - { - if (!isset(self::$ignoredLines[$filename])) { - self::$ignoredLines[$filename] = array(); + for ($i = $line; $i < $line + $count; $i++) { + self::$ignoredLines[$filename][$i] = TRUE; + } - $ignore = FALSE; - $stop = FALSE; - $tokens = PHP_Token_Stream_CachingFactory::get($filename)->tokens(); + // 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; - foreach ($tokens as $token) { - switch (get_class($token)) { + case 'PHP_Token_INTERFACE': + case 'PHP_Token_TRAIT': case 'PHP_Token_CLASS': case 'PHP_Token_FUNCTION': { $docblock = $token->getDocblock(); - $endLine = $token->getEndLine(); 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; case 'PHP_Token_COMMENT': { $_token = trim($token); - if ($_token == '// @codeCoverageIgnoreStart' || - $_token == '//@codeCoverageIgnoreStart') { + if ($_token == '// @codeCoverageIgnore' || + $_token == '//@codeCoverageIgnore') { + $ignore = TRUE; + $stop = TRUE; + } + + else if ($_token == '// @codeCoverageIgnoreStart' || + $_token == '//@codeCoverageIgnoreStart') { $ignore = TRUE; } @@ -315,73 +235,17 @@ public static function getLinesToBeIgnored($filename) return self::$ignoredLines[$filename]; } - /** - * 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 a filesystem safe version of the passed filename. - * This function does not operate on full paths, just filenames. - * - * @param string $filename - * @return string - * @author Michael Lively Jr. - */ - public static function getSafeFilename($filename) - { - /* characters allowed: A-Z, a-z, 0-9, _ and . */ - return preg_replace('#[^\w.]#', '_', $filename); - } - /** * @param float $a * @param float $b * @return float ($a / $b) * 100 */ - public static function percent($a, $b, $asString = FALSE) + public static function percent($a, $b, $asString = FALSE, $fixedWidth = FALSE) { + if ($asString && $b == 0) { + return ''; + } + if ($b > 0) { $percent = ($a / $b) * 100; } else { @@ -389,255 +253,13 @@ public static function percent($a, $b, $asString = FALSE) } if ($asString) { - return sprintf('%01.2F', $percent); - } else { - return $percent; - } - } - - /** - * 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 - */ - public static 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++) { - $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]); - - if (empty($paths[$i][0])) { - $paths[$i][0] = DIRECTORY_SEPARATOR; + if ($fixedWidth) { + return sprintf('%6.2F%%', $percent); } - } - - $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 $commonPath; - } - - /** - * Returns the package information of a user-defined class. - * - * @param array $parts - * @param string $join - * @return string - */ - protected static function arrayToName(array $parts, $join = '\\') - { - $result = ''; - - if (count($parts) > 1) { - array_pop($parts); - - $result = join($join, $parts); - } - - return $result; - } - - /** - * @param string $coveredElement - * @return array - */ - protected static function resolveCoversToReflectionObjects($coveredElement) - { - $codeToCoverList = array(); - - if (strpos($coveredElement, '::') !== FALSE) { - list($className, $methodName) = explode('::', $coveredElement); - - if ($methodName[0] == '<') { - $classes = array($className); - - foreach ($classes as $className) { - if (!class_exists($className) && - !interface_exists($className)) { - throw new RuntimeException( - 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)) && - method_exists($className, $methodName))) { - throw new RuntimeException( - sprintf( - 'Trying to @cover not existing method "%s::%s".', - $className, - $methodName - ) - ); - } - - $codeToCoverList[] = new ReflectionMethod( - $className, $methodName - ); - } - } - } + return sprintf('%01.2F%%', $percent); } 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)) { - throw new RuntimeException( - sprintf( - 'Trying to @cover not existing class or ' . - 'interface "%s".', - $className - ) - ); - } - - $codeToCoverList[] = new ReflectionClass($className); - } + return $percent; } - - return $codeToCoverList; } } diff --git a/libs/PHPUnit/PHP/CodeCoverage/Util/InvalidArgumentHelper.php b/libs/PHPUnit/PHP/CodeCoverage/Util/InvalidArgumentHelper.php new file mode 100644 index 0000000..cd80a58 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/CodeCoverage/Version.php b/libs/PHPUnit/PHP/CodeCoverage/Version.php new file mode 100644 index 0000000..3912a16 --- /dev/null +++ b/libs/PHPUnit/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.3'; + 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/libs/PHPUnit/PHP/Timer.php b/libs/PHPUnit/PHP/Timer.php index afecddf..0f2cc2b 100644 --- a/libs/PHPUnit/PHP/Timer.php +++ b/libs/PHPUnit/PHP/Timer.php @@ -2,7 +2,7 @@ /** * PHP_Timer * - * Copyright (c) 2010, Sebastian Bergmann . + * Copyright (c) 2010-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHP * @subpackage Timer * @author Sebastian Bergmann - * @copyright 2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,16 +49,24 @@ * @package PHP * @subpackage Timer * @author Sebastian Bergmann - * @copyright 2002-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.0 + * @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. */ @@ -120,7 +128,7 @@ public static function secondsToTimeString($time) */ public static function timeSinceStartOfRequest() { - return self::secondsToTimeString(time() - $_SERVER['REQUEST_TIME']); + return self::secondsToTimeString(time() - self::$requestTime); } /** @@ -137,3 +145,15 @@ public static function resourceUsage() ); } } + +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/libs/PHPUnit/PHP/Timer/Autoload.php b/libs/PHPUnit/PHP/Timer/Autoload.php new file mode 100644 index 0000000..0184b9d --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/Timer/Autoload.php.in b/libs/PHPUnit/PHP/Timer/Autoload.php.in new file mode 100644 index 0000000..77e7b03 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/Token.php b/libs/PHPUnit/PHP/Token.php index 1581e0e..ebd4ce7 100644 --- a/libs/PHPUnit/PHP/Token.php +++ b/libs/PHPUnit/PHP/Token.php @@ -2,7 +2,7 @@ /** * php-token-stream * - * Copyright (c) 2009-2010, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,20 +36,18 @@ * * @package PHP_TokenStream * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/Token/Exception.php'; - /** * A PHP token. * * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.1 + * @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 */ @@ -112,15 +110,48 @@ 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(); + $tokens = $this->tokenStream->tokens(); + $currentLineNumber = $tokens[$this->id]->getLine(); + $prevLineNumber = $currentLineNumber - 1; - for ($i = $this->id - 2; $i > $this->id - 6; $i -= 2) { - if (isset($tokens[$i]) && - $tokens[$i] instanceof PHP_Token_DOC_COMMENT) { - return (string)$tokens[$i]; + 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]; } } @@ -131,7 +162,8 @@ public function getEndTokenId() $tokens = $this->tokenStream->tokens(); while ($this->endTokenId === NULL && isset($tokens[$i])) { - if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY) { + if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || + $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { $block++; } @@ -143,6 +175,14 @@ public function getEndTokenId() } } + 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++; } @@ -157,13 +197,97 @@ 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); + } + } -class PHP_Token_REQUIRE_ONCE extends PHP_Token {} -class PHP_Token_REQUIRE extends PHP_Token {} +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 {} -class PHP_Token_INCLUDE 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 {} @@ -235,8 +359,10 @@ 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_TokenWithScope +class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility { protected $arguments; protected $ccn; @@ -291,6 +417,19 @@ public function getName() $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; } @@ -333,9 +472,14 @@ public function getSignature() return $this->signature; } - $this->signature = ''; + if ($this->getName() == 'anonymous function') { + $this->signature = 'anonymous function'; + $i = $this->id + 1; + } else { + $this->signature = ''; + $i = $this->id + 2; + } - $i = $this->id + 2; $tokens = $this->tokenStream->tokens(); while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { @@ -367,8 +511,10 @@ 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_TokenWithScope +class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility { + protected $interfaces; + public function getName() { return (string)$this->tokenStream[$this->id + 2]; @@ -379,6 +525,62 @@ 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()) { @@ -389,15 +591,54 @@ public function getParent() $tokens = $this->tokenStream->tokens(); $className = (string)$tokens[$i]; - while (!$tokens[$i+1] instanceof PHP_Token_WHITESPACE) { + 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 {} @@ -405,6 +646,7 @@ 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 {} @@ -421,7 +663,7 @@ 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_Token +class PHP_Token_NAMESPACE extends PHP_TokenWithScope { public function getName() { diff --git a/libs/PHPUnit/PHP/Token/Stream.php b/libs/PHPUnit/PHP/Token/Stream.php index 60aba66..77ba0dc 100644 --- a/libs/PHPUnit/PHP/Token/Stream.php +++ b/libs/PHPUnit/PHP/Token/Stream.php @@ -2,7 +2,7 @@ /** * php-token-stream * - * Copyright (c) 2009-2010, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,20 +36,18 @@ * * @package PHP_TokenStream * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/Token.php'; - /** * A stream of PHP tokens. * * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.1 + * @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 */ @@ -89,6 +87,11 @@ class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator '`' => 'PHP_Token_BACKTICK' ); + /** + * @var string + */ + protected $filename; + /** * @var array */ @@ -114,6 +117,21 @@ class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator */ protected $functions; + /** + * @var array + */ + protected $includes; + + /** + * @var array + */ + protected $interfaces; + + /** + * @var array + */ + protected $traits; + /** * Constructor. * @@ -122,12 +140,44 @@ class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator public function __construct($sourceCode) { if (is_file($sourceCode)) { - $sourceCode = file_get_contents($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. @@ -171,20 +221,6 @@ protected function scan($sourceCode) $this->linesOfCode['cloc']; } - /** - * @return string - */ - public function __toString() - { - $buffer = ''; - - foreach ($this as $token) { - $buffer .= $token; - } - - return $buffer; - } - /** * @return integer */ @@ -210,7 +246,7 @@ public function getClasses() return $this->classes; } - $this->parseClassesFunctions(); + $this->parse(); return $this->classes; } @@ -224,48 +260,190 @@ public function getFunctions() return $this->functions; } - $this->parseClassesFunctions(); + $this->parse(); return $this->functions; } - protected function parseClassesFunctions() + /** + * @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->classes = array(); - $this->functions = array(); - $class = FALSE; - $classEndLine = FALSE; + $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_CLASS': { - $class = $token->getName(); - $classEndLine = $token->getEndLine(); + case 'PHP_Token_HALT_COMPILER': { + return; + } + break; - $this->classes[$class] = array( + 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' => $classEndLine + '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() + 'ccn' => $token->getCCN(), + 'file' => $this->filename ); - if ($class === FALSE) { + if ($class === FALSE && + $trait === FALSE && + $interface === FALSE) { $this->functions[$name] = $tmp; - } else { + } + + 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; @@ -275,6 +453,18 @@ protected function parseClassesFunctions() $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; } diff --git a/libs/PHPUnit/PHP/Token/Stream/Autoload.php b/libs/PHPUnit/PHP/Token/Stream/Autoload.php new file mode 100644 index 0000000..9895b34 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHP/Token/Exception.php b/libs/PHPUnit/PHP/Token/Stream/Autoload.php.in similarity index 78% rename from libs/PHPUnit/PHP/Token/Exception.php rename to libs/PHPUnit/PHP/Token/Stream/Autoload.php.in index 3791aa3..ebd488b 100644 --- a/libs/PHPUnit/PHP/Token/Exception.php +++ b/libs/PHPUnit/PHP/Token/Stream/Autoload.php.in @@ -37,20 +37,29 @@ * @package PHP_TokenStream * @author Sebastian Bergmann * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @since File available since Release 1.0.0 - */ - -/** - * Exception class. - * - * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.1 + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/php-token-stream/tree - * @since Class available since Release 1.0.0 + * @since File available since Release 1.1.0 */ -class PHP_Token_Exception extends RuntimeException -{ -} + +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/libs/PHPUnit/PHP/Token/Stream/CachingFactory.php b/libs/PHPUnit/PHP/Token/Stream/CachingFactory.php index e1a43b9..6ff2c5d 100644 --- a/libs/PHPUnit/PHP/Token/Stream/CachingFactory.php +++ b/libs/PHPUnit/PHP/Token/Stream/CachingFactory.php @@ -2,7 +2,7 @@ /** * php-token-stream * - * Copyright (c) 2009-2010, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,20 +36,18 @@ * * @package PHP_TokenStream * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -require_once 'PHP/Token/Stream.php'; - /** * A caching factory for token stream objects. * * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.1 + * @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 */ @@ -72,4 +70,16 @@ public static function get($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/libs/PHPUnit/PHP/Token/Stream/TextUI/Command.php b/libs/PHPUnit/PHP/Token/Stream/TextUI/Command.php deleted file mode 100644 index 8bcc4a8..0000000 --- a/libs/PHPUnit/PHP/Token/Stream/TextUI/Command.php +++ /dev/null @@ -1,181 +0,0 @@ -. - * 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-license.php BSD License - * @since File available since Release 1.0.0 - */ - -require_once 'PHP/Token/Stream.php'; -require_once 'ezc/Base/base.php'; - -function __autoload($className) -{ - ezcBase::autoload($className); -} - -/** - * TextUI frontend for PHP_TokenStream. - * - * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.0.1 - * @link http://github.com/sebastianbergmann/php-token-stream/tree - * @since Class available since Release 1.0.0 - */ -class PHP_Token_Stream_TextUI_Command -{ - /** - * Main method. - */ - public static function main() - { - $input = new ezcConsoleInput; - - $input->registerOption( - new ezcConsoleOption( - 'h', - 'help', - ezcConsoleInput::TYPE_NONE, - NULL, - FALSE, - '', - '', - array(), - array(), - FALSE, - FALSE, - TRUE - ) - ); - - $input->registerOption( - new ezcConsoleOption( - 'v', - 'version', - ezcConsoleInput::TYPE_NONE, - NULL, - FALSE, - '', - '', - array(), - array(), - FALSE, - FALSE, - TRUE - ) - ); - - try { - $input->process(); - } - - catch (ezcConsoleOptionException $e) { - print $e->getMessage() . "\n"; - exit(1); - } - - if ($input->getOption('help')->value) { - self::showHelp(); - exit(0); - } - - else if ($input->getOption('version')->value) { - self::printVersionString(); - exit(0); - } - - $arguments = $input->getArguments(); - - if (empty($arguments)) { - self::showHelp(); - exit(1); - } - - self::printVersionString(); - - print "Line Token Text\n" . - str_repeat('-', 79) . "\n"; - - foreach (new PHP_Token_Stream($arguments[0]) as $token) { - if ($token instanceof PHP_Token_WHITESPACE) { - $text = ''; - } else { - $text = str_replace(array("\r", "\n"), '', (string)$token); - - if (strlen($text) > 40) { - $text = explode("\n", wordwrap($text, 40)); - $text = $text[0]; - } - } - - printf( - "%5d %-30s %s\n", - $token->getLine(), - str_replace('PHP_Token_', '', get_class($token)), - $text - ); - } - } - - /** - * Shows the help. - */ - protected static function showHelp() - { - self::printVersionString(); - - print << - - --help Prints this usage information. - --version Prints the version and exits. - -EOT; - } - - /** - * Prints the version string. - */ - protected static function printVersionString() - { - print "phptok 1.0.1 by Sebastian Bergmann.\n\n"; - } -} -?> diff --git a/libs/PHPUnit/PHPUnit/Autoload.php b/libs/PHPUnit/PHPUnit/Autoload.php index 19ea365..a775c1d 100644 --- a/libs/PHPUnit/PHPUnit/Autoload.php +++ b/libs/PHPUnit/PHPUnit/Autoload.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,54 +36,173 @@ * * @package PHPUnit * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'PHPUnit/Util/Filesystem.php'; -require_once 'PHP/CodeCoverage/Filter.php'; +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'; -if (!function_exists('phpunit_autoload')) { - function phpunit_autoload($class) - { - if (strpos($class, 'PHPUnit_') === 0) { - $file = str_replace('_', '/', $class) . '.php'; - $file = PHPUnit_Util_Filesystem::fileExistsInIncludePath($file); +spl_autoload_register( + function ($class) + { + static $classes = NULL; + static $path = NULL; - if ($file) { - require_once $file; - } - } - } + 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' + ); - spl_autoload_register('phpunit_autoload'); + $path = dirname(__FILE__); + } - $dir = dirname(__FILE__); - $filter = PHP_CodeCoverage_Filter::getInstance(); + $cn = strtolower($class); - $filter->addDirectoryToBlacklist( - $dir . '/Extensions', '.php', '', 'PHPUNIT', FALSE - ); + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); - $filter->addDirectoryToBlacklist( - $dir . '/Framework', '.php', '', 'PHPUNIT', FALSE - ); - - $filter->addDirectoryToBlacklist( - $dir . '/Runner', '.php', '', 'PHPUNIT', FALSE - ); +if (stream_resolve_include_path('PHP/Invoker/Autoload.php')) { + require_once 'PHP/Invoker/Autoload.php'; +} - $filter->addDirectoryToBlacklist( - $dir . '/TextUI', '.php', '', 'PHPUNIT', FALSE - ); +if (stream_resolve_include_path('PHPUnit/Extensions/Database/Autoload.php')) { + require_once 'PHPUnit/Extensions/Database/Autoload.php'; +} - $filter->addDirectoryToBlacklist( - $dir . '/Util', '.php', '', 'PHPUNIT', FALSE - ); +if (stream_resolve_include_path('PHPUnit/Extensions/SeleniumCommon/Autoload.php')) { + require_once 'PHPUnit/Extensions/SeleniumCommon/Autoload.php'; +} - $filter->addFileToBlacklist(__FILE__, 'PHPUNIT', FALSE); +else if (stream_resolve_include_path('PHPUnit/Extensions/SeleniumTestCase/Autoload.php')) { + require_once 'PHPUnit/Extensions/SeleniumTestCase/Autoload.php'; +} - unset($dir, $filter); +if (stream_resolve_include_path('PHPUnit/Extensions/Story/Autoload.php')) { + require_once 'PHPUnit/Extensions/Story/Autoload.php'; } diff --git a/libs/PHPUnit/PHPUnit/Autoload.php.in b/libs/PHPUnit/PHPUnit/Autoload.php.in new file mode 100644 index 0000000..1d16b94 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Extensions/GroupTestSuite.php b/libs/PHPUnit/PHPUnit/Extensions/GroupTestSuite.php index bb84df0..80f8360 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/GroupTestSuite.php +++ b/libs/PHPUnit/PHPUnit/Extensions/GroupTestSuite.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Extensions * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -58,9 +58,8 @@ * @package PHPUnit * @subpackage Extensions * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Extensions/OutputTestCase.php b/libs/PHPUnit/PHPUnit/Extensions/OutputTestCase.php deleted file mode 100644 index 8da2346..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/OutputTestCase.php +++ /dev/null @@ -1,202 +0,0 @@ -. - * 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 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.0.0 - */ - -/** - * A TestCase that expects a specified output. - * - * @package PHPUnit - * @subpackage Extensions - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.0.0 - */ -abstract class PHPUnit_Extensions_OutputTestCase extends PHPUnit_Framework_TestCase -{ - /** - * @var string - */ - protected $expectedRegex = NULL; - - /** - * @var string - */ - protected $expectedString = NULL; - - /** - * @var string - */ - protected $output = ''; - - /** - * @var boolean - */ - protected $obActive = FALSE; - - /** - * @var mixed - */ - protected $outputCallback = FALSE; - - /** - * @return bool - */ - public function setOutputCallback($callback) - { - if (is_callable($callback)) { - $this->outputCallback = $callback; - return TRUE; - } - - return FALSE; - } - - /** - * @return string - */ - public function normalizeOutput($buffer) - { - return str_replace("\r", '', $buffer); - } - - /** - * @return string - */ - public function getActualOutput() - { - if (!$this->obActive) { - return $this->output; - } else { - return ob_get_contents(); - } - } - - /** - * @return string - */ - public function expectedRegex() - { - return $this->expectedRegex; - } - - /** - * @param string $expectedRegex - */ - public function expectOutputRegex($expectedRegex) - { - if ($this->expectedString !== NULL) { - throw new PHPUnit_Framework_Exception; - } - - if (is_string($expectedRegex) || is_null($expectedRegex)) { - $this->expectedRegex = $expectedRegex; - } - } - - /** - * @return string - */ - public function expectedString() - { - return $this->expectedString; - } - - /** - * @param string $expectedString - */ - public function expectOutputString($expectedString) - { - if ($this->expectedRegex !== NULL) { - throw new PHPUnit_Framework_Exception; - } - - if (is_string($expectedString) || is_null($expectedString)) { - $this->expectedString = $expectedString; - } - } - - /** - * @return mixed - * @throws RuntimeException - */ - protected function runTest() - { - ob_start(); - $this->obActive = TRUE; - - try { - $testResult = parent::runTest(); - } - - catch (Exception $e) { - ob_end_clean(); - $this->obActive = FALSE; - throw $e; - } - - 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->obActive = FALSE; - - if ($this->expectedRegex !== NULL) { - $this->assertRegExp($this->expectedRegex, $this->output); - $this->expectedRegex = NULL; - } - - else if ($this->expectedString !== NULL) { - $this->assertEquals($this->expectedString, $this->output); - $this->expectedString = NULL; - } - - return $testResult; - } -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase.php b/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase.php index 71b381d..16336bc 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase.php +++ b/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,33 +37,26 @@ * @package PHPUnit * @subpackage Extensions_PhptTestCase * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 (PHPUnit_Util_Filesystem::fileExistsInIncludePath('PEAR/RunTest.php')) { +if (stream_resolve_include_path('PEAR/RunTest.php')) { $currentErrorReporting = error_reporting(E_ERROR | E_WARNING | E_PARSE); - PHPUnit_Util_Filesystem::collectStart(); require_once 'PEAR/RunTest.php'; error_reporting($currentErrorReporting); - - PHPUnit_Util_Filesystem::collectEndAndAddToBlacklist(); } -require_once 'PHP/CodeCoverage.php'; -require_once 'PHP/Timer.php'; - /** * Wrapper to run .phpt test cases. * * @package PHPUnit * @subpackage Extensions_PhptTestCase * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -195,16 +188,10 @@ public function run(PHPUnit_Framework_TestResult $result = NULL, array $options '.phpt', '.php', $base ); - if (file_exists($phpFile)) { - PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist( - $phpFile, 'TESTS' - ); - } - if (is_object($buffer) && $buffer instanceof PEAR_Error) { $result->addError( $this, - new RuntimeException($buffer->getMessage()), + new PHPUnit_Framework_Exception($buffer->getMessage()), $time ); } @@ -214,11 +201,16 @@ public function run(PHPUnit_Framework_TestResult $result = NULL, array $options } else if ($buffer != 'PASSED') { + $expContent = file_get_contents($expFile); + $outContent = file_get_contents($outFile); + $result->addFailure( $this, - PHPUnit_Framework_ComparisonFailure::diffEqual( - file_get_contents($expFile), - file_get_contents($outFile) + new PHPUnit_Framework_ComparisonFailure( + $expContent, + $outContent, + $expContent, + $outContent ), $time ); diff --git a/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase/Logger.php b/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase/Logger.php index 0d27a5e..6b33a41 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase/Logger.php +++ b/libs/PHPUnit/PHPUnit/Extensions/PhptTestCase/Logger.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Extensions_PhptTestCase * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Extensions_PhptTestCase * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Extensions/PhptTestSuite.php b/libs/PHPUnit/PHPUnit/Extensions/PhptTestSuite.php index c5f7d44..ad394f2 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/PhptTestSuite.php +++ b/libs/PHPUnit/PHPUnit/Extensions/PhptTestSuite.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,23 +37,20 @@ * @package PHPUnit * @subpackage Extensions_PhptTestCase * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ -require_once 'File/Iterator/Factory.php'; - /** * Suite for .phpt test cases. * * @package PHPUnit * @subpackage Extensions_PhptTestCase * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -65,19 +62,18 @@ class PHPUnit_Extensions_PhptTestSuite extends PHPUnit_Framework_TestSuite * @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 InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function __construct($directory, array $options = array()) { if (is_string($directory) && is_dir($directory)) { $this->setName($directory); - $iterator = File_Iterator_Factory::getFileIterator( - $directory, '.phpt' - ); + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray($directory, '.phpt'); - foreach ($iterator as $testFile) { - $this->addTestFile($testFile->getPathname(), TRUE, $options); + foreach ($files as $file) { + $this->addTestFile($file, $options); } } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'directory name'); diff --git a/libs/PHPUnit/PHPUnit/Extensions/RepeatedTest.php b/libs/PHPUnit/PHPUnit/Extensions/RepeatedTest.php index 34d4882..261520b 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/RepeatedTest.php +++ b/libs/PHPUnit/PHPUnit/Extensions/RepeatedTest.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,8 +36,8 @@ * * @package PHPUnit * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -47,9 +47,8 @@ * * @package PHPUnit * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -89,7 +88,7 @@ class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator * @param array $groups * @param array $excludeGroups * @param boolean $processIsolation - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $filter = FALSE, array $groups = array(), array $excludeGroups = array(), $processIsolation = FALSE) { @@ -127,7 +126,7 @@ public function count() * * @param PHPUnit_Framework_TestResult $result * @return PHPUnit_Framework_TestResult - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function run(PHPUnit_Framework_TestResult $result = NULL) { @@ -135,7 +134,9 @@ public function run(PHPUnit_Framework_TestResult $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, diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/HTML.php b/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/HTML.php deleted file mode 100644 index 805092d..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/HTML.php +++ /dev/null @@ -1,212 +0,0 @@ -. - * 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_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.0 - */ - -/** - * Prints stories in HTML format. - * - * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.0 - */ -class PHPUnit_Extensions_Story_ResultPrinter_HTML extends PHPUnit_Extensions_Story_ResultPrinter -{ - /** - * @var boolean - */ - protected $printsHTML = TRUE; - - /** - * @var integer - */ - protected $id = 0; - - /** - * @var string - */ - protected $scenarios = ''; - - /** - * @var string - */ - protected $templatePath; - - /** - * Constructor. - * - * @param mixed $out - * @throws InvalidArgumentException - */ - public function __construct($out = NULL) - { - parent::__construct($out); - - $this->templatePath = sprintf( - '%s%sTemplate%s', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR - ); - } - - /** - * Handler for 'start class' event. - * - * @param string $name - */ - protected function startClass($name) - { - $scenarioHeaderTemplate = new Text_Template( - $this->templatePath . 'scenario_header.html' - ); - - $scenarioHeaderTemplate->setVar( - array( - 'name' => $this->currentTestClassPrettified - ) - ); - - $this->scenarios .= $scenarioHeaderTemplate->render(); - } - - /** - * Handler for 'on test' event. - * - * @param string $name - * @param boolean $success - * @param array $steps - */ - protected function onTest($name, $success = TRUE, array $steps = array()) - { - if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) { - $scenarioStatus = 'scenarioFailed'; - } - - else if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED) { - $scenarioStatus = 'scenarioSkipped'; - } - - else if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE) { - $scenarioStatus = 'scenarioIncomplete'; - } - - else { - $scenarioStatus = 'scenarioSuccess'; - } - - $lastStepName = ''; - $stepsBuffer = ''; - - foreach ($steps as $step) { - $currentStepName = $step->getName(); - - if ($lastStepName == $currentStepName) { - $stepText = 'and'; - } else { - $stepText = $currentStepName; - } - - $lastStepName = $currentStepName; - - $stepTemplate = new Text_Template( - $this->templatePath . 'step.html' - ); - - $stepTemplate->setVar( - array( - 'text' => $stepText, - 'action' => $step->getAction() . ' ' . $step->getArguments(TRUE), - ) - ); - - $stepsBuffer .= $stepTemplate->render(); - } - - $scenarioTemplate = new Text_Template( - $this->templatePath . 'scenario.html' - ); - - $scenarioTemplate->setVar( - array( - 'id' => ++$this->id, - 'name' => $name, - 'scenarioStatus' => $scenarioStatus, - 'steps' => $stepsBuffer, - ) - ); - - $this->scenarios .= $scenarioTemplate->render(); - } - - /** - * Handler for 'end run' event. - * - */ - protected function endRun() - { - $scenariosTemplate = new Text_Template( - $this->templatePath . 'scenarios.html' - ); - - $scenariosTemplate->setVar( - array( - 'scenarios' => $this->scenarios, - 'successfulScenarios' => $this->successful, - 'failedScenarios' => $this->failed, - 'skippedScenarios' => $this->skipped, - 'incompleteScenarios' => $this->incomplete - ) - ); - - $this->write($scenariosTemplate->render()); - } -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario.html.dist b/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario.html.dist deleted file mode 100644 index caa149a..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario.html.dist +++ /dev/null @@ -1,13 +0,0 @@ - - -

    [+] {name}

    - - - - - -{steps} -
    - - - diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario_header.html.dist b/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario_header.html.dist deleted file mode 100644 index 7b205d1..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenario_header.html.dist +++ /dev/null @@ -1,6 +0,0 @@ - - -

    {name}

    - - - diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenarios.html.dist b/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenarios.html.dist deleted file mode 100644 index 21bf38e..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/scenarios.html.dist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - -{scenarios} - - - -
    -

    [+] Summary:

    - -
    - - diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/step.html.dist b/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/step.html.dist deleted file mode 100644 index bcdce3f..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Template/step.html.dist +++ /dev/null @@ -1,6 +0,0 @@ - - {text} - {action} -   - - diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Text.php b/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Text.php deleted file mode 100644 index 1a3cc91..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter/Text.php +++ /dev/null @@ -1,150 +0,0 @@ -. - * 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_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.0 - */ - -/** - * Prints stories in HTML format. - * - * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.0 - */ -class PHPUnit_Extensions_Story_ResultPrinter_Text extends PHPUnit_Extensions_Story_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 - * @param array $steps - */ - protected function onTest($name, $success = TRUE, array $steps = array()) - { - if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) { - $scenarioStatus = 'failed'; - } - - else if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED) { - $scenarioStatus = 'skipped'; - } - - else if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE) { - $scenarioStatus = 'incomplete'; - } - - else { - $scenarioStatus = 'successful'; - } - - $this->write( - sprintf( - " [%s] %s\n\n", - $scenarioStatus == 'successful' ? 'x' : ' ', - $name - ) - ); - - $lastStepName = ''; - $stepsBuffer = ''; - - foreach ($steps as $step) { - $currentStepName = $step->getName(); - - if ($lastStepName == $currentStepName) { - $stepText = 'and'; - } else { - $stepText = $currentStepName; - } - - $lastStepName = $currentStepName; - - $this->write( - sprintf( - " %5s %s %s\n", - $stepText, - $step->getAction(), - $step->getArguments(TRUE) - ) - ); - } - - $this->write("\n"); - } - - /** - * Handler for 'end run' event. - * - */ - protected function endRun() - { - $this->write( - sprintf( - "Scenarios: %d, Failed: %d, Skipped: %d, Incomplete: %d.\n", - - $this->successful + $this->failed + - $this->skipped + $this->incomplete, - $this->failed, - $this->skipped, - $this->incomplete - ) - ); - } -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/Scenario.php b/libs/PHPUnit/PHPUnit/Extensions/Story/Scenario.php deleted file mode 100644 index 3ac94cf..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/Scenario.php +++ /dev/null @@ -1,189 +0,0 @@ -. - * 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_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.0 - */ - -/** - * A scenario. - * - * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.0 - */ -class PHPUnit_Extensions_Story_Scenario -{ - /** - * @var PHPUnit_Extensions_Story_TestCase - */ - protected $test; - - /** - * @var array - */ - protected $steps = array(); - - /** - * @var string - */ - protected $lastCalledMethod; - - /** - * Constructor. - * - * @param PHPUnit_Extensions_Story_TestCase $caller - */ - public function __construct($test) - { - if ($test instanceof PHPUnit_Extensions_Story_TestCase || - $test instanceof PHPUnit_Extensions_Story_SeleniumTestCase) { - $this->test = $test; - } else { - throw new Exception('$test must either be PHPUnit_Extensions_Story_TestCase or PHPUnit_Extensions_Story_SeleniumTestCase'); - } - } - - /** - * Adds a "Given" step to the scenario. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - public function given($arguments) - { - return $this->addStep(new PHPUnit_Extensions_Story_Given($arguments)); - } - - /** - * Adds a "When" step to the scenario. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - public function when($arguments) - { - return $this->addStep(new PHPUnit_Extensions_Story_When($arguments)); - } - - /** - * Adds a "Then" step to the scenario. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - public function then($arguments) - { - return $this->addStep(new PHPUnit_Extensions_Story_Then($arguments)); - } - - /** - * Add another step of the same type as the step that was added before. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - public function _and($arguments) - { - $lastCalledStepClass = get_class($this->steps[count($this->steps)-1]); - - return $this->addStep(new $lastCalledStepClass($arguments)); - } - - /** - * Runs this scenario. - * - * @param array $world - */ - public function run(array &$world) - { - foreach ($this->steps as $step) - { - if ($step instanceof PHPUnit_Extensions_Story_Given) { - $this->test->runGiven( - $world, $step->getAction(), $step->getArguments() - ); - } - - else if ($step instanceof PHPUnit_Extensions_Story_When) { - $this->test->runWhen( - $world, $step->getAction(), $step->getArguments() - ); - } - - else { - $this->test->runThen( - $world, $step->getAction(), $step->getArguments() - ); - } - } - } - - /** - * Adds a step to the scenario. - * - * @param PHPUnit_Extensions_Story_Step $step - * @return PHPUnit_Extensions_Story_TestCase - */ - protected function addStep(PHPUnit_Extensions_Story_Step $step) - { - $this->steps[] = $step; - - return $this->test; - } - - /** - * Returns the steps of this scenario. - * - * @return array - */ - public function getSteps() - { - return $this->steps; - } -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/TestCase.php b/libs/PHPUnit/PHPUnit/Extensions/Story/TestCase.php deleted file mode 100644 index 6be29c6..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/TestCase.php +++ /dev/null @@ -1,210 +0,0 @@ -. - * 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_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.0 - */ - -/** - * A story test case. - * - * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.0 - */ -abstract class PHPUnit_Extensions_Story_TestCase extends PHPUnit_Framework_TestCase -{ - /** - * @var PHPUnit_Extensions_Story_Scenario - */ - protected $scenario; - - /** - * @var array - */ - protected $world = array(); - - /** - * 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 = '') - { - parent::__construct($name, $data, $dataName); - $this->scenario = new PHPUnit_Extensions_Story_Scenario($this); - } - - /** - * @method PHPUnit_Extensions_Story_Step and($contextOrOutcome) - */ - public function __call($command, $arguments) - { - switch ($command) { - case 'and': { - return $this->scenario->_and($arguments); - } - break; - - default: { - throw new BadMethodCallException( - "Method $command not defined." - ); - } - } - } - - /** - * Returns this test's scenario. - * - * @return PHPUnit_Extensions_Story_Scenario - */ - public function getScenario() - { - return $this->scenario; - } - - /** - * - * - */ - protected function notImplemented($action) - { - if (strstr($action, ' ')) { - $this->markTestIncomplete("step: $action not implemented."); - } - - throw new BadMethodCallException("Method $action not defined."); - } - - /** - * Adds a "Given" step to the scenario. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - protected function given($context) - { - return $this->scenario->given(func_get_args()); - } - - /** - * Adds a "When" step to the scenario. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - protected function when($event) - { - return $this->scenario->when(func_get_args()); - } - - /** - * Adds a "Then" step to the scenario. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - protected function then($outcome) - { - return $this->scenario->then(func_get_args()); - } - - /** - * Add another step of the same type as the step that was added before. - * - * @param array $arguments - * @return PHPUnit_Extensions_Story_TestCase - */ - protected function _and($contextOrOutcome) - { - return $this->scenario->_and(func_get_args()); - } - - /** - * Run this test's scenario. - * - * @return mixed - * @throws RuntimeException - */ - protected function runTest() - { - $testResult = parent::runTest(); - $this->scenario->run($this->world); - return $testResult; - } - - /** - * Implementation for "Given" steps. - * - * @param array $world - * @param string $action - * @param array $arguments - */ - abstract protected function runGiven(&$world, $action, $arguments); - - /** - * Implementation for "When" steps. - * - * @param array $world - * @param string $action - * @param array $arguments - */ - abstract protected function runWhen(&$world, $action, $arguments); - - /** - * Implementation for "Then" steps. - * - * @param array $world - * @param string $action - * @param array $arguments - */ - abstract protected function runThen(&$world, $action, $arguments); -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/TestDecorator.php b/libs/PHPUnit/PHPUnit/Extensions/TestDecorator.php index 50a4e25..8bdfb16 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/TestDecorator.php +++ b/libs/PHPUnit/PHPUnit/Extensions/TestDecorator.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Extensions * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -53,9 +53,8 @@ * @package PHPUnit * @subpackage Extensions * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -136,7 +135,6 @@ public function getTest() * * @param PHPUnit_Framework_TestResult $result * @return PHPUnit_Framework_TestResult - * @throws InvalidArgumentException */ public function run(PHPUnit_Framework_TestResult $result = NULL) { diff --git a/libs/PHPUnit/PHPUnit/Extensions/TicketListener.php b/libs/PHPUnit/PHPUnit/Extensions/TicketListener.php index c9e2d70..4be38e1 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/TicketListener.php +++ b/libs/PHPUnit/PHPUnit/Extensions/TicketListener.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,8 +39,8 @@ * @author Sean Coates * @author Raphael Stolt * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -52,9 +52,8 @@ * @subpackage Extensions_TicketListener * @author Sean Coates * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Extensions/TicketListener/GitHub.php b/libs/PHPUnit/PHPUnit/Extensions/TicketListener/GitHub.php deleted file mode 100644 index 11c277a..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/TicketListener/GitHub.php +++ /dev/null @@ -1,204 +0,0 @@ -. - * 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 Raphael Stolt - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.5.0 - */ - -/** - * A ticket listener that interacts with the GitHub issue API. - * - * @package PHPUnit - * @subpackage Extensions_TicketListener - * @author Raphael Stolt - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.5.0 - */ -class PHPUnit_Extensions_TicketListener_GitHub extends PHPUnit_Extensions_TicketListener -{ - const STATUS_CLOSE = 'closed'; - const STATUS_REOPEN = 'reopened'; - - private $username; - private $apiToken; - private $repository; - private $apiPath = 'http://github.com/api/v2/json/issues'; - private $printTicketStateChanges; - - /** - * @param string $username The username associated with the GitHub account. - * @param string $apiToken The API token associated with the GitHub account. - * @param string $repository The repository of the system under test (SUT) on GitHub. - * @param string $printTicketChanges Boolean flag to print the ticket state - * changes in the test result. - * @throws RuntimeException - */ - public function __construct($username, $apiToken, $repository, $printTicketStateChanges = FALSE) - { - if (!extension_loaded('curl')) { - throw new RuntimeException('ext/curl is not available'); - } - - if (!extension_loaded('json')) { - throw new RuntimeException('ext/json is not available'); - } - - $this->username = $username; - $this->apiToken = $apiToken; - $this->repository = $repository; - $this->printTicketStateChanges = $printTicketStateChanges; - } - - /** - * @param integer $ticketId - * @return string - * @throws RuntimeException - */ - public function getTicketInfo($ticketId = NULL) - { - if (!is_numeric($ticketId)) { - return array('status' => 'invalid_ticket_id'); - } - - $ticket = $this->callGitHub( - $this->apiPath . '/show/' . $this->username . '/' . - $this->repository . '/' . $ticketId, - TRUE - ); - - if ($ticket['state'] === 'open') { - return array('status' => 'new'); - } - - if ($ticket['state'] === 'closed') { - return array('status' => 'closed'); - } - - if ($ticket['state'] === 'unknown_ticket') { - return array('status' => 'unknown_ticket'); - } - } - - /** - * @param string $ticketId The ticket number of the ticket under test (TUT). - * @param string $statusToBe The status of the TUT after running the associated test. - * @param string $message The additional message for the TUT. - * @param string $resolution The resolution for the TUT. - * @throws RuntimeException - */ - protected function updateTicket($ticketId, $statusToBe, $message, $resolution) - { - $acceptedResponseIssueStates = array('open', 'closed'); - - if ($statusToBe === self::STATUS_CLOSE) { - $apiEndpoint = $this->apiPath . '/close/' . - $this->username . '/' . $this->repository . '/' . - $ticketId; - } - - else if ($statusToBe === self::STATUS_REOPEN) { - $apiEndpoint = $this->apiPath . '/reopen/' . - $this->username . '/' . $this->repository . '/' . - $ticketId; - } - - if (isset($apiEndpoint)) { - $ticket = $this->callGitHub($apiEndpoint); - - if (!in_array($ticket['state'], $acceptedResponseIssueStates)) { - throw new RuntimeException( - 'Received an unaccepted issue state from the GitHub Api' - ); - } - - if ($this->printTicketStateChanges) { - printf( - "\nUpdating GitHub issue #%d, status: %s\n", - $ticketId, - $statusToBe - ); - } - } - } - - /** - * @param string $apiEndpoint API endpoint to call against the GitHub issue API. - * @param boolean $isShowMethodCall Show method of the GitHub issue API is called? - * @return array - * @throws RuntimeException - */ - private function callGitHub($apiEndpoint, $isShowMethodCall = FALSE) - { - $curlHandle = curl_init(); - - curl_setopt($curlHandle, CURLOPT_URL, $apiEndpoint); - curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($curlHandle, CURLOPT_FAILONERROR, TRUE); - curl_setopt($curlHandle, CURLOPT_FRESH_CONNECT, TRUE); - curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($curlHandle, CURLOPT_HTTPPROXYTUNNEL, TRUE); - curl_setopt($curlHandle, CURLOPT_USERAGENT, __CLASS__); - curl_setopt( - $curlHandle, - CURLOPT_POSTFIELDS, - 'login=' . $this->username . '&token=' . $this->apiToken - ); - - $response = curl_exec($curlHandle); - - if (!$response && $isShowMethodCall) { - return array('state' => 'unknown_ticket'); - } - - if (!$response) { - throw new RuntimeException(curl_error($curlHandle)); - } - - curl_close($curlHandle); - - $issue = (array)json_decode($response); - - return (array)$issue['issue']; - } -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/TicketListener/GoogleCode.php b/libs/PHPUnit/PHPUnit/Extensions/TicketListener/GoogleCode.php deleted file mode 100644 index 96e7e1d..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/TicketListener/GoogleCode.php +++ /dev/null @@ -1,275 +0,0 @@ -. - * 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 Jan Sorgalla - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.5.0 - */ - -/** - * A ticket listener that interacts with the GoogleCode issue API. - * - * @package PHPUnit - * @subpackage Extensions_TicketListener - * @author Jan Sorgalla - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.5.0 - */ -class PHPUnit_Extensions_TicketListener_GoogleCode extends PHPUnit_Extensions_TicketListener -{ - private $email; - private $password; - private $project; - - private $statusClosed; - private $statusReopened; - - private $printTicketStateChanges; - - private $authUrl = 'https://www.google.com/accounts/ClientLogin'; - private $apiBaseUrl = 'http://code.google.com/feeds/issues/p/%s/issues'; - private $authToken; - - /** - * @param string $email The email associated with the Google account. - * @param string $password The password associated with the Google account. - * @param string $project The project name of the system under test (SUT) on Google Code. - * @param string $printTicketChanges Boolean flag to print the ticket state changes in the test result. - * @param string $statusClosed The status name of the closed state. - * @param string $statusReopened The status name of the reopened state. - * @throws RuntimeException - */ - public function __construct($email, $password, $project, $printTicketStateChanges = FALSE, $statusClosed = 'Fixed', $statusReopened = 'Started') - { - if (!extension_loaded('curl')) { - throw new RuntimeException('ext/curl is not available'); - } - - if (!extension_loaded('simplexml')) { - throw new RuntimeException('ext/simplexml is not available'); - } - - $this->email = $email; - $this->password = $password; - $this->project = $project; - $this->statusClosed = $statusClosed; - $this->statusReopened = $statusReopened; - $this->printTicketStateChanges = $printTicketStateChanges; - $this->apiBaseUrl = sprintf($this->apiBaseUrl, $project); - } - - /** - * @param integer $ticketId - * @return array - * @throws RuntimeException - */ - public function getTicketInfo($ticketId = NULL) - { - if (!is_numeric($ticketId)) { - return array('status' => 'invalid_ticket_id'); - } - - $url = $this->apiBaseUrl . '/full/' . $ticketId; - $header = array( - 'Authorization: GoogleLogin auth=' . $this->getAuthToken() - ); - - list($status, $response) = $this->callGoogleCode($url, $header); - - if ($status != 200 || !$response) { - return array('state' => 'unknown_ticket'); - } - - $ticket = new SimpleXMLElement(str_replace("xmlns=", "ns=", $response)); - $result = $ticket->xpath('//issues:state'); - $state = (string)$result[0]; - - if ($state === 'open') { - return array('status' => 'new'); - } - - if ($state === 'closed') { - return array('status' => 'closed'); - } - - return array('status' => $state); - } - - /** - * @param string $ticketId The ticket number of the ticket under test (TUT). - * @param string $statusToBe The status of the TUT after running the associated test. - * @param string $message The additional message for the TUT. - * @param string $resolution The resolution for the TUT. - * @throws RuntimeException - */ - protected function updateTicket($ticketId, $statusToBe, $message, $resolution) - { - $url = $this->apiBaseUrl . '/' . $ticketId . '/comments/full'; - - $header = array( - 'Authorization: GoogleLogin auth=' . $this->getAuthToken(), - 'Content-Type: application/atom+xml' - ); - - if ($statusToBe == 'closed') { - $ticketStatus = $this->statusClosed; - } else { - $ticketStatus = $this->statusReopened; - } - - list($author,) = explode('@', $this->email); - - $post = '' . - '' . - ' ' . htmlspecialchars($message, ENT_COMPAT, 'UTF-8') . '' . - ' ' . - ' ' . htmlspecialchars($author, ENT_COMPAT, 'UTF-8') . '' . - ' ' . - ' ' . - ' ' . htmlspecialchars($ticketStatus, ENT_COMPAT, 'UTF-8') . '' . - ' ' . - ''; - - list($status, $response) = $this->callGoogleCode($url, $header, $post); - - if ($status != 201) { - throw new RuntimeException('Updating GoogleCode issue failed with status code ' . $status); - } - - if ($this->printTicketStateChanges) { - printf( - "\nUpdating GoogleCode issue #%d, status: %s\n", - $ticketId, - $statusToBe - ); - } - } - - /** - * @return string The auth token - * @throws RuntimeException - */ - private function getAuthToken() - { - if (NULL !== $this->authToken) { - return $this->authToken; - } - - $header = array( - 'Content-Type: application/x-www-form-urlencoded', - ); - - $post = array( - 'accountType' => 'GOOGLE', - 'Email' => $this->email, - 'Passwd' => $this->password, - 'service' => 'code', - 'source' => 'PHPUnit-TicketListener_GoogleCode-' . PHPUnit_Runner_Version::id(), - ); - - list($status, $response) = $this->callGoogleCode( - $this->authUrl, - $header, - http_build_query($post, NULL, '&') - ); - - if ($status != 200) { - throw new RuntimeException('Google account authentication failed'); - } - - foreach (explode("\n", $response) as $line) { - if (strpos(trim($line), 'Auth') === 0) { - list($name, $token) = explode('=', $line); - $this->authToken = trim($token); - break; - } - } - - if (NULL === $this->authToken) { - throw new RuntimeException('Could not detect auth token in response'); - } - - return $this->authToken; - } - - /** - * @param string $url URL to call - * @param array $header Header - * @param string $post Post data - * @return array - */ - private function callGoogleCode($url, array $header = NULL, $post = NULL) - { - $curlHandle = curl_init(); - - curl_setopt($curlHandle, CURLOPT_URL, $url); - curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($curlHandle, CURLOPT_FAILONERROR, TRUE); - curl_setopt($curlHandle, CURLOPT_FRESH_CONNECT, TRUE); - curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($curlHandle, CURLOPT_HTTPPROXYTUNNEL, TRUE); - curl_setopt($curlHandle, CURLOPT_USERAGENT, __CLASS__); - curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, FALSE); - - if (NULL !== $header) { - curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $header); - } - - if (NULL !== $post) { - curl_setopt($curlHandle, CURLOPT_POST, TRUE); - curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $post); - } - - $response = curl_exec($curlHandle); - $status = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); - - if (!$response) { - throw new RuntimeException(curl_error($curlHandle)); - } - - curl_close($curlHandle); - - return array($status, $response); - } -} diff --git a/libs/PHPUnit/PHPUnit/Extensions/TicketListener/Trac.php b/libs/PHPUnit/PHPUnit/Extensions/TicketListener/Trac.php deleted file mode 100644 index 1e323cc..0000000 --- a/libs/PHPUnit/PHPUnit/Extensions/TicketListener/Trac.php +++ /dev/null @@ -1,189 +0,0 @@ -. - * 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 Testing - * @package PHPUnit - * @author Graham Christensen - * @author Sean Coates - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since Class available since Release 3.5.4 - */ - -/** - * A ticket listener that interacts with Trac. - * - * - * - * - * - * - * - * - * trac_username - * trac_password - * - * - * 127.0.0.1/trac/login/xmlrpc - * - * - * - * - * - * - * @category Testing - * @package PHPUnit - * @author Graham Christensen - * @author Sean Coates - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.5.4 - */ -class PHPUnit_Extensions_TicketListener_Trac extends PHPUnit_Extensions_TicketListener -{ - protected $username; - protected $password; - protected $hostpath; - protected $scheme; - private $printTicketStateChanges; - - /** - * Constructor - * - * @param string $username Trac-XMLRPC username - * @param string $password Trac-XMLRPC password - * @param string $hostpath Trac-XMLRPC Host+Path (e.g. example.com/trac/login/xmlrpc) - * @param string $scheme Trac scheme (http or https) - * @param bool $printTicketStateChanges To display changes or not - */ - public function __construct($username, $password, $hostpath, $scheme = 'http', $printTicketStateChanges = FALSE) - { - $this->username = $username; - $this->password = $password; - $this->hostpath = $hostpath; - $this->scheme = $scheme; - $this->printTicketStateChanges = $printTicketStateChanges; - } - - /** - * Get the status of a ticket message - * - * @param integer $ticketId The ticket ID - * @return array('status' => $status) ($status = new|closed|unknown_ticket) - */ - public function getTicketInfo($ticketId = NULL) - { - if (!is_numeric($ticketId)) { - return array('status' => 'invalid_ticket_id'); - } - - try { - $info = $this->getClient()->get($ticketId); - - switch ($info[3]['status']) { - case 'closed': { - return array('status' => 'closed'); - } - break; - - case 'new': - case 'reopened': { - return array('status' => 'new'); - } - break; - - default: { - return array('status' => 'unknown_ticket'); - } - } - } - - catch (Exception $e) { - return array('status' => 'unknown_ticket'); - } - } - - /** - * Update a ticket with a new status - * - * @param string $ticketId The ticket number of the ticket under test (TUT). - * @param string $statusToBe The status of the TUT after running the associated test. - * @param string $message The additional message for the TUT. - * @param string $resolution The resolution for the TUT. - */ - protected function updateTicket($ticketId, $statusToBe, $message, $resolution) - { - $change = array('status' => $statusToBe, 'resolution' => $resolution); - - $this->getClient()->update((int)$ticketId, $message, $change); - - if ($this->printTicketStateChanges) { - printf( - "\nUpdating Trac issue #%d, status: %s\n", $ticketId, $statusToBe - ); - } - } - - /** - * Get a Trac XML_RPC2 client - * - * @return XML_RPC2_Client - */ - protected function getClient() - { - if (!PHPUnit_Util_Filesystem::fileExistsInIncludePath('XML/RPC2/Client.php')) { - throw new PHPUnit_Framework_Exception('PEAR/XML_RPC2 is not available.'); - } - - require_once 'XML/RPC2/Client.php'; - - $url = sprintf( - '%s://%s:%s@%s', - $this->scheme, - $this->username, - $this->password, - $this->hostpath - ); - - return XML_RPC2_Client::create($url, array('prefix' => 'ticket.')); - } -} diff --git a/libs/PHPUnit/PHPUnit/Framework/Assert.php b/libs/PHPUnit/PHPUnit/Framework/Assert.php index 02ce907..66e2301 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Assert.php +++ b/libs/PHPUnit/PHPUnit/Framework/Assert.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -66,17 +65,22 @@ abstract class PHPUnit_Framework_Assert * Asserts that an array has a specified key. * * @param mixed $key - * @param array $array + * @param array|ArrayAccess $array * @param string $message * @since Method available since Release 3.0.0 */ - public static function assertArrayHasKey($key, array $array, $message = '') + 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); @@ -87,17 +91,22 @@ public static function assertArrayHasKey($key, array $array, $message = '') * Asserts that an array does not have a specified key. * * @param mixed $key - * @param array $array + * @param array|ArrayAccess $array * @param string $message * @since Method available since Release 3.0.0 */ - public static function assertArrayNotHasKey($key, array $array, $message = '') + 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) @@ -113,14 +122,15 @@ public static function assertArrayNotHasKey($key, array $array, $message = '') * @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) + 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 + $needle, $checkForObjectIdentity ); } @@ -148,15 +158,17 @@ public static function assertContains($needle, $haystack, $message = '', $ignore * @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) + public static function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) { self::assertContains( $needle, self::readAttribute($haystackClassOrObject, $haystackAttributeName), $message, - $ignoreCase + $ignoreCase, + $checkForObjectIdentity ); } @@ -167,14 +179,17 @@ public static function assertAttributeContains($needle, $haystackAttributeName, * @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) + 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) + new PHPUnit_Framework_Constraint_TraversableContains( + $needle, $checkForObjectIdentity + ) ); } @@ -204,15 +219,17 @@ public static function assertNotContains($needle, $haystack, $message = '', $ign * @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) + public static function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) { self::assertNotContains( $needle, self::readAttribute($haystackClassOrObject, $haystackAttributeName), $message, - $ignoreCase + $ignoreCase, + $checkForObjectIdentity ); } @@ -247,6 +264,31 @@ public static function assertContainsOnly($type, $haystack, $isNativeType = NULL ); } + /** + * 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. @@ -323,6 +365,96 @@ public static function assertAttributeNotContainsOnly($type, $haystackAttributeN ); } + /** + * 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. * @@ -1195,192 +1327,108 @@ public static function assertAttributeNotInternalType($expected, $attributeName, } /** - * Asserts that a variable is of a given type. + * Asserts that a string matches a given regular expression. * - * @param string $expected - * @param mixed $actual + * @param string $pattern + * @param string $string * @param string $message - * @deprecated */ - public static function assertType($expected, $actual, $message = '') + public static function assertRegExp($pattern, $string, $message = '') { - PHPUnit_Util_DeprecatedFeature_Logger::log( - 'assertType() will be removed in PHPUnit 3.6 and should no longer ' . - 'be used. assertInternalType() should be used for asserting ' . - 'internal types such as "integer" or "string" whereas ' . - 'assertInstanceOf() should be used for asserting that an object is ' . - 'an instance of a specified class or interface.' - ); - - if (is_string($expected)) { - if (PHPUnit_Util_Type::isType($expected)) { - $constraint = new PHPUnit_Framework_Constraint_IsType( - $expected - ); - } - - else 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 { + if (!is_string($pattern)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } - self::assertThat($actual, $constraint, $message); - } + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } - /** - * 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.4.0 - * @deprecated - */ - public static function assertAttributeType($expected, $attributeName, $classOrObject, $message = '') - { - PHPUnit_Util_DeprecatedFeature_Logger::log( - 'assertAttributeType() will be removed in PHPUnit 3.6 and should ' . - 'no longer be used. assertAttributeInternalType() should be used ' . - 'for asserting internal types such as "integer" or "string" ' . - 'whereas assertAttributeInstanceOf() should be used for asserting ' . - 'that an object is an instance of a specified class or interface.' - ); + $constraint = new PHPUnit_Framework_Constraint_PCREMatch($pattern); - self::assertType( - $expected, - self::readAttribute($classOrObject, $attributeName), - $message - ); + self::assertThat($string, $constraint, $message); } /** - * Asserts that a variable is not of a given type. + * Asserts that a string does not match a given regular expression. * - * @param string $expected - * @param mixed $actual + * @param string $pattern + * @param string $string * @param string $message - * @since Method available since Release 2.2.0 - * @deprecated + * @since Method available since Release 2.1.0 */ - public static function assertNotType($expected, $actual, $message = '') + public static function assertNotRegExp($pattern, $string, $message = '') { - PHPUnit_Util_DeprecatedFeature_Logger::log( - 'assertNotType() will be removed in PHPUnit 3.6 and should no ' . - 'longer be used. assertNotInternalType() should be used for ' . - 'asserting internal types such as "integer" or "string" whereas ' . - 'assertNotInstanceOf() should be used for asserting that an object ' . - 'is not an instance of a specified class or interface.' - ); - - if (is_string($expected)) { - if (PHPUnit_Util_Type::isType($expected)) { - $constraint = new PHPUnit_Framework_Constraint_Not( - new PHPUnit_Framework_Constraint_IsType($expected) - ); - } - - else 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 { + if (!is_string($pattern)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } - self::assertThat($actual, $constraint, $message); - } + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } - /** - * 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.4.0 - * @deprecated - */ - public static function assertAttributeNotType($expected, $attributeName, $classOrObject, $message = '') - { - PHPUnit_Util_DeprecatedFeature_Logger::log( - 'assertAttributeNotType() will be removed in PHPUnit 3.6 and ' . - 'should no longer be used. assertAttributeNotInternalType() should ' . - 'be used for asserting internal types such as "integer" or ' . - '"string" whereas assertAttributeNotInstanceOf() should be used ' . - 'for asserting that an object is an instance of a specified class ' . - 'or interface.' + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_PCREMatch($pattern) ); - self::assertNotType( - $expected, - self::readAttribute($classOrObject, $attributeName), - $message - ); + self::assertThat($string, $constraint, $message); } /** - * Asserts that a string matches a given regular expression. + * Assert that the size of two arrays (or `Countable` or `Iterator` objects) + * is the same. * - * @param string $pattern - * @param string $string - * @param string $message + * @param array|Countable|Iterator $expected + * @param array|Countable|Iterator $actual + * @param string $message */ - public static function assertRegExp($pattern, $string, $message = '') + public static function assertSameSize($expected, $actual, $message = '') { - if (!is_string($pattern)) { - throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + if (!$expected instanceof Countable && + !$expected instanceof Iterator && + !is_array($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable'); } - if (!is_string($string)) { - throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + if (!$actual instanceof Countable && + !$actual instanceof Iterator && + !is_array($actual)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable'); } - $constraint = new PHPUnit_Framework_Constraint_PCREMatch($pattern); - - self::assertThat($string, $constraint, $message); + self::assertThat( + $actual, + new PHPUnit_Framework_Constraint_SameSize($expected), + $message + ); } /** - * Asserts that a string does not match a given regular expression. + * Assert that the size of two arrays (or `Countable` or `Iterator` objects) + * is not the same. * - * @param string $pattern - * @param string $string - * @param string $message - * @since Method available since Release 2.1.0 + * @param array|Countable|Iterator $expected + * @param array|Countable|Iterator $actual + * @param string $message */ - public static function assertNotRegExp($pattern, $string, $message = '') + public static function assertNotSameSize($expected, $actual, $message = '') { - if (!is_string($pattern)) { - throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + if (!$expected instanceof Countable && + !$expected instanceof Iterator && + !is_array($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable'); } - if (!is_string($string)) { - throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + 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_PCREMatch($pattern) + new PHPUnit_Framework_Constraint_SameSize($expected) ); - self::assertThat($string, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -1714,38 +1762,38 @@ public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXm } /** - * Asserts that a hierarchy of DOMNodes matches. + * Asserts that a hierarchy of DOMElements matches. * - * @param DOMNode $expectedNode - * @param DOMNode $actualNode + * @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(DOMNode $expectedNode, DOMNode $actualNode, $checkAttributes = FALSE, $message = '') + public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = FALSE, $message = '') { self::assertEquals( - $expectedNode->tagName, - $actualNode->tagName, + $expectedElement->tagName, + $actualElement->tagName, $message ); if ($checkAttributes) { self::assertEquals( - $expectedNode->attributes->length, - $actualNode->attributes->length, + $expectedElement->attributes->length, + $actualElement->attributes->length, sprintf( '%s%sNumber of attributes on node "%s" does not match', $message, !empty($message) ? "\n" : '', - $expectedNode->tagName + $expectedElement->tagName ) ); - for ($i = 0 ; $i < $expectedNode->attributes->length; $i++) { - $expectedAttribute = $expectedNode->attributes->item($i); - $actualAttribute = $actualNode->attributes->getNamedItem( + for ($i = 0 ; $i < $expectedElement->attributes->length; $i++) { + $expectedAttribute = $expectedElement->attributes->item($i); + $actualAttribute = $actualElement->attributes->getNamedItem( $expectedAttribute->name ); @@ -1756,31 +1804,31 @@ public static function assertEqualXMLStructure(DOMNode $expectedNode, DOMNode $a $message, !empty($message) ? "\n" : '', $expectedAttribute->name, - $expectedNode->tagName + $expectedElement->tagName ) ); } } } - PHPUnit_Util_XML::removeCharacterDataNodes($expectedNode); - PHPUnit_Util_XML::removeCharacterDataNodes($actualNode); + PHPUnit_Util_XML::removeCharacterDataNodes($expectedElement); + PHPUnit_Util_XML::removeCharacterDataNodes($actualElement); self::assertEquals( - $expectedNode->childNodes->length, - $actualNode->childNodes->length, + $expectedElement->childNodes->length, + $actualElement->childNodes->length, sprintf( '%s%sNumber of child nodes of "%s" differs', $message, !empty($message) ? "\n" : '', - $expectedNode->tagName + $expectedElement->tagName ) ); - for ($i = 0; $i < $expectedNode->childNodes->length; $i++) { + for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { self::assertEqualXMLStructure( - $expectedNode->childNodes->item($i), - $actualNode->childNodes->item($i), + $expectedElement->childNodes->item($i), + $actualElement->childNodes->item($i), $checkAttributes, $message ); @@ -1898,7 +1946,7 @@ public static function assertSelectEquals($selector, $content, $count, $actual, self::assertTrue($counted <= $count['<='], $message); } } else { - throw new InvalidArgumentException(); + throw new PHPUnit_Framework_Exception; } } @@ -1947,7 +1995,7 @@ public static function assertSelectEquals($selector, $content, $count, $actual, * * // Matcher that asserts that there is a "span" tag with content matching * // the regular expression pattern. - * $matcher = array('tag' => 'span', 'content' => '/Try P(HP|ython)/'); + * $matcher = array('tag' => 'span', 'content' => 'regexp:/Try P(HP|ython)/'); * * // Matcher that asserts that there is a "span" with an "list" class * // attribute. @@ -2083,9 +2131,164 @@ public static function assertThat($value, PHPUnit_Framework_Constraint $constrai { self::$count += count($constraint); - if (!$constraint->evaluate($value)) { - $constraint->fail($value, $message); + $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); } /** @@ -2170,6 +2373,16 @@ 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. * @@ -2211,13 +2424,14 @@ public static function attribute(PHPUnit_Framework_Constraint $constraint, $attr * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher * object. * - * @param mixed $value + * @param mixed $value + * @param boolean $checkForObjectIdentity * @return PHPUnit_Framework_Constraint_TraversableContains * @since Method available since Release 3.0.0 */ - public static function contains($value) + public static function contains($value, $checkForObjectIdentity = TRUE) { - return new PHPUnit_Framework_Constraint_TraversableContains($value); + return new PHPUnit_Framework_Constraint_TraversableContains($value, $checkForObjectIdentity); } /** @@ -2233,6 +2447,18 @@ 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. * @@ -2517,20 +2743,6 @@ public static function fail($message = '') throw new PHPUnit_Framework_AssertionFailedError($message); } - /** - * Fails a test with a synthetic error. - * - * @param string $message - * @param string $file - * @param integer $line - * @param array $trace - * @throws PHPUnit_Framework_SyntheticError - */ - public static function syntheticFail($message = '', $file = '', $line = 0, $trace = array()) - { - throw new PHPUnit_Framework_SyntheticError($message, 0, $file, $line, $trace); - } - /** * Returns the value of an attribute of a class or an object. * This also works for attributes that are declared protected or private. @@ -2538,7 +2750,7 @@ public static function syntheticFail($message = '', $file = '', $line = 0, $trac * @param mixed $classOrObject * @param string $attributeName * @return mixed - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public static function readAttribute($classOrObject, $attributeName) { diff --git a/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php b/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php index b8de30e..057a950 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php +++ b/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -113,11 +113,12 @@ function assertArrayNotHasKey($key, array $array, $message = '') * @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) +function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) { - return PHPUnit_Framework_Assert::assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message, $ignoreCase); + return PHPUnit_Framework_Assert::assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message, $ignoreCase, $checkForObjectIdentity); } /** @@ -136,6 +137,21 @@ function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackCla 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. @@ -260,11 +276,12 @@ function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actual * @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) +function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) { - return PHPUnit_Framework_Assert::assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message, $ignoreCase); + return PHPUnit_Framework_Assert::assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message, $ignoreCase, $checkForObjectIdentity); } /** @@ -284,6 +301,21 @@ function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystack 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. @@ -357,21 +389,6 @@ function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrO return PHPUnit_Framework_Assert::assertAttributeNotSame($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.4.0 - * @deprecated - */ -function assertAttributeNotType($expected, $attributeName, $classOrObject, $message = '') -{ - return PHPUnit_Framework_Assert::assertAttributeNotType($expected, $attributeName, $classOrObject, $message); -} - /** * Asserts that a variable and an attribute of an object have the same type * and value. @@ -386,21 +403,6 @@ function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObje return PHPUnit_Framework_Assert::assertAttributeSame($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.4.0 - * @deprecated - */ -function assertAttributeType($expected, $attributeName, $classOrObject, $message = '') -{ - return PHPUnit_Framework_Assert::assertAttributeType($expected, $attributeName, $classOrObject, $message); -} - /** * Asserts that a class has a specified attribute. * @@ -460,11 +462,12 @@ function assertClassNotHasStaticAttribute($attributeName, $className, $message = * @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) +function assertContains($needle, $haystack, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) { - return PHPUnit_Framework_Assert::assertContains($needle, $haystack, $message, $ignoreCase); + return PHPUnit_Framework_Assert::assertContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity); } /** @@ -481,6 +484,18 @@ 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. * @@ -494,18 +509,18 @@ function assertEmpty($actual, $message = '') } /** - * Asserts that a hierarchy of DOMNodes matches. + * Asserts that a hierarchy of DOMElements matches. * - * @param DOMNode $expectedNode - * @param DOMNode $actualNode + * @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(DOMNode $expectedNode, DOMNode $actualNode, $checkAttributes = FALSE, $message = '') +function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = FALSE, $message = '') { - return PHPUnit_Framework_Assert::assertEqualXMLStructure($expectedNode, $actualNode, $checkAttributes, $message); + return PHPUnit_Framework_Assert::assertEqualXMLStructure($expectedElement, $actualElement, $checkAttributes, $message); } /** @@ -644,6 +659,84 @@ 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. * @@ -677,11 +770,12 @@ function assertLessThanOrEqual($expected, $actual, $message = '') * @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) +function assertNotContains($needle, $haystack, $message = '', $ignoreCase = FALSE, $checkForObjectIdentity = TRUE) { - return PHPUnit_Framework_Assert::assertNotContains($needle, $haystack, $message, $ignoreCase); + return PHPUnit_Framework_Assert::assertNotContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity); } /** @@ -698,6 +792,18 @@ 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. * @@ -791,6 +897,19 @@ 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(). * @@ -810,20 +929,6 @@ function assertNotTag($matcher, $actual, $message = '', $isHtml = TRUE) return PHPUnit_Framework_Assert::assertNotTag($matcher, $actual, $message, $isHtml); } -/** - * Asserts that a variable is not of a given type. - * - * @param string $expected - * @param mixed $actual - * @param string $message - * @since Method available since Release 2.2.0 - * @deprecated - */ -function assertNotType($expected, $actual, $message = '') -{ - return PHPUnit_Framework_Assert::assertNotType($expected, $actual, $message); -} - /** * Asserts that a variable is NULL. * @@ -887,6 +992,19 @@ 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. @@ -1135,7 +1253,7 @@ function assertStringStartsWith($prefix, $string, $message = '') * * // Matcher that asserts that there is a "span" tag with content matching * // the regular expression pattern. - * $matcher = array('tag' => 'span', 'content' => '/Try P(HP|ython)/'); + * $matcher = array('tag' => 'span', 'content' => 'regexp:/Try P(HP|ython)/'); * * // Matcher that asserts that there is a "span" with an "list" class * // attribute. @@ -1257,19 +1375,6 @@ function assertTrue($condition, $message = '') return PHPUnit_Framework_Assert::assertTrue($condition, $message); } -/** - * Asserts that a variable is of a given type. - * - * @param string $expected - * @param mixed $actual - * @param string $message - * @deprecated - */ -function assertType($expected, $actual, $message = '') -{ - return PHPUnit_Framework_Assert::assertType($expected, $actual, $message); -} - /** * Asserts that two XML files are equal. * @@ -1434,13 +1539,14 @@ function classHasStaticAttribute($attributeName) * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher * object. * - * @param mixed $value + * @param mixed $value + * @param boolean $checkForObjectIdentity * @return PHPUnit_Framework_Constraint_TraversableContains * @since Method available since Release 3.0.0 */ -function contains($value) +function contains($value, $checkForObjectIdentity = TRUE) { - return PHPUnit_Framework_Assert::contains($value); + return PHPUnit_Framework_Assert::contains($value, $checkForObjectIdentity); } /** @@ -1590,6 +1696,16 @@ 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. * @@ -1769,6 +1885,19 @@ 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(); +} + /** * * @@ -1781,6 +1910,18 @@ 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. * diff --git a/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php.in b/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php.in index 1c5089d..ada7bfc 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php.in +++ b/libs/PHPUnit/PHPUnit/Framework/Assert/Functions.php.in @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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/libs/PHPUnit/PHPUnit/Framework/AssertionFailedError.php b/libs/PHPUnit/PHPUnit/Framework/AssertionFailedError.php index c8d3f15..b3ab961 100644 --- a/libs/PHPUnit/PHPUnit/Framework/AssertionFailedError.php +++ b/libs/PHPUnit/PHPUnit/Framework/AssertionFailedError.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,13 +49,12 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 Exception implements PHPUnit_Framework_SelfDescribing +class PHPUnit_Framework_AssertionFailedError extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing { /** * Wrapper for getMessage() which is declared as final. diff --git a/libs/PHPUnit/PHPUnit/Framework/Comparator.php b/libs/PHPUnit/PHPUnit/Framework/Comparator.php new file mode 100644 index 0000000..c63398b --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Array.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Array.php new file mode 100644 index 0000000..9e3d4ab --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/DOMDocument.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/DOMDocument.php new file mode 100644 index 0000000..400204e --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Double.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Double.php new file mode 100644 index 0000000..e2d8d77 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Exception.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Exception.php new file mode 100644 index 0000000..e4b2e97 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/MockObject.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/MockObject.php new file mode 100644 index 0000000..52af9b1 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Numeric.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Numeric.php new file mode 100644 index 0000000..2ab9b06 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Object.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Object.php new file mode 100644 index 0000000..cba32c8 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Resource.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Resource.php new file mode 100644 index 0000000..30f751f --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Scalar.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Scalar.php new file mode 100644 index 0000000..e13bf75 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/SplObjectStorage.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/SplObjectStorage.php new file mode 100644 index 0000000..efbf2c7 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Comparator/Type.php b/libs/PHPUnit/PHPUnit/Framework/Comparator/Type.php new file mode 100644 index 0000000..5cfcf56 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/ComparatorFactory.php b/libs/PHPUnit/PHPUnit/Framework/ComparatorFactory.php new file mode 100644 index 0000000..7a1ecb0 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure.php b/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure.php index 31dac8e..5482337 100644 --- a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure.php +++ b/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,13 +50,13 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ -abstract class PHPUnit_Framework_ComparisonFailure extends PHPUnit_Framework_AssertionFailedError +class PHPUnit_Framework_ComparisonFailure extends PHPUnit_Framework_AssertionFailedError { /** * Expected value of the retrieval which does not match $actual. @@ -69,6 +70,18 @@ abstract class PHPUnit_Framework_ComparisonFailure extends PHPUnit_Framework_Ass */ 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 */ @@ -86,16 +99,19 @@ abstract class PHPUnit_Framework_ComparisonFailure extends PHPUnit_Framework_Ass * * @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, $identical = FALSE, $message = '') + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = FALSE, $message = '') { - $this->expected = $expected; - $this->actual = $actual; - $this->identical = $identical; - $this->message = $message; + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; } /** @@ -115,94 +131,36 @@ public function getExpected() } /** - * @return boolean + * @return string */ - public function identical() + public function getActualAsString() { - return $this->identical; + return $this->actualAsString; } /** - * Figures out which diff class to use for the input types then - * instantiates that class and returns the object. - * @note The diff is type sensitive, if the type differs only the types - * are shown. - * - * @param mixed $expected Expected value retrieved. - * @param mixed $actual Actual value retrieved. - * @param string $message A string which is prefixed on all returned lines - * in the difference output. - * @return PHPUnit_Framework_ComparisonFailure + * @return string */ - public static function diffIdentical($expected, $actual, $message = '') + public function getExpectedAsString() { - if (gettype($expected) !== gettype($actual)) { - return new PHPUnit_Framework_ComparisonFailure_Type( - $expected, $actual, TRUE, $message - ); - } - - else if (is_array($expected) && is_array($actual)) { - return new PHPUnit_Framework_ComparisonFailure_Array( - $expected, $actual, TRUE, $message - ); - } - - else if (is_object($expected) && is_object($actual)) { - return new PHPUnit_Framework_ComparisonFailure_Object( - $expected, $actual, TRUE, $message - ); - } - - else if (is_string($expected) && !is_object($actual)) { - return new PHPUnit_Framework_ComparisonFailure_String( - $expected, $actual, TRUE, $message - ); - } - - else if (is_null($expected) || is_scalar($expected)) { - return new PHPUnit_Framework_ComparisonFailure_Scalar( - $expected, $actual, TRUE, $message - ); - } + return $this->expectedAsString; } /** - * Figures out which diff class to use for the input types then - * instantiates that class and returns the object. - * @note The diff is not type sensitive, if the type differs the $actual - * value will be converted to the same type as the $expected. - * - * @param mixed $expected Expected value retrieved. - * @param mixed $actual Actual value retrieved. - * @param string $message A string which is prefixed on all returned lines - * in the difference output. - * @return PHPUnit_Framework_ComparisonFailure + * @return string */ - public static function diffEqual($expected, $actual, $message = '') + public function getDiff() { - if (is_array($expected) && is_array($actual)) { - return new PHPUnit_Framework_ComparisonFailure_Array( - $expected, $actual, FALSE, $message - ); - } - - else if (is_object($expected) && is_object($actual)) { - return new PHPUnit_Framework_ComparisonFailure_Object( - $expected, $actual, FALSE, $message - ); - } - - else if (is_string($expected) && !is_object($actual)) { - return new PHPUnit_Framework_ComparisonFailure_String( - $expected, $actual, FALSE, $message - ); - } + return $this->actualAsString || $this->expectedAsString + ? PHPUnit_Util_Diff::diff($this->expectedAsString, $this->actualAsString) + : ''; + } - else if (is_null($expected) || is_scalar($expected)) { - return new PHPUnit_Framework_ComparisonFailure_Scalar( - $expected, $actual, FALSE, $message - ); - } + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Array.php b/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Array.php deleted file mode 100644 index f77b409..0000000 --- a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Array.php +++ /dev/null @@ -1,140 +0,0 @@ -. - * 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_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.0.0 - */ - -/** - * Thrown when an assertion for array equality failed. - * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.0.0 - */ -class PHPUnit_Framework_ComparisonFailure_Array extends PHPUnit_Framework_ComparisonFailure -{ - /** - * Returns a string describing the difference between the expected and the - * actual array. - * - * @return string - */ - public function toString() - { - if (!$this->identical) { - ksort($this->expected); - ksort($this->actual); - } - - $diff = PHPUnit_Util_Diff::diff( - print_r($this->expected, TRUE), - print_r($this->actual, TRUE) - ); - - if ($diff !== FALSE) { - return trim($diff); - } - - // Fallback: Either diff is not available or the print_r() output for - // the expected and the actual array are equal (but the arrays are not). - - $expectedOnly = array(); - $actualOnly = array(); - $diff = ''; - - foreach ($this->expected as $expectedKey => $expectedValue) { - if (!array_key_exists($expectedKey, $this->actual)) { - $expectedOnly[] = $expectedKey; - continue; - } - - if ($expectedValue === $this->actual[$expectedKey]) { - continue; - } - - $diffObject = PHPUnit_Framework_ComparisonFailure::diffIdentical( - $expectedValue, - $this->actual[$expectedKey], - sprintf( - '%sarray key %s: ', - - $this->message, - PHPUnit_Util_Type::toString($expectedKey) - ) - ); - - $diff .= $diffObject->toString() . "\n"; - } - - foreach ($this->actual as $actualKey => $actualValue) { - if (!array_key_exists($actualKey, $this->expected)) { - $actualOnly[] = $actualKey; - continue; - } - } - - foreach ($expectedOnly as $expectedKey) { - $diff .= sprintf( - "array key %s: only in expected %s\n", - - PHPUnit_Util_Type::toString($expectedKey), - PHPUnit_Util_Type::toString($this->expected[$expectedKey]) - ); - } - - foreach ($actualOnly as $actualKey) { - $diff .= sprintf( - "array key %s: only in actual %s\n", - - PHPUnit_Util_Type::toString($actualKey), - PHPUnit_Util_Type::toString($this->actual[$actualKey]) - ); - } - - return $diff; - } -} diff --git a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Object.php b/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Object.php deleted file mode 100644 index 04f6215..0000000 --- a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Object.php +++ /dev/null @@ -1,170 +0,0 @@ -. - * 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_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.0.0 - */ - -/** - * Thrown when an assertion for object equality failed. - * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.0.0 - */ -class PHPUnit_Framework_ComparisonFailure_Object extends PHPUnit_Framework_ComparisonFailure -{ - /** - * Returns a string describing the difference between the expected and the - * actual object. - * - * @return string - */ - public function toString() - { - $diff = PHPUnit_Util_Diff::diff( - print_r($this->expected, TRUE), - print_r($this->actual, TRUE) - ); - - if ($diff !== FALSE) { - return trim($diff); - } - - // Fallback: Either diff is not available or the print_r() output for - // the expected and the actual object are equal (but the objects are - // not). - - $expectedClass = get_class($this->expected); - $actualClass = get_class($this->actual); - - if ($expectedClass !== $actualClass) { - return sprintf( - "%s%sexpected class <%s>\n" . - '%sgot class <%s>', - - $this->message, - ($this->message != '') ? ' ' : '', - $expectedClass, - ($this->message != '') ? str_repeat(' ', strlen($this->message) + 1) : '', - $actualClass - ); - } else { - $expectedReflection = new ReflectionClass($expectedClass); - $actualReflection = new ReflectionClass($actualClass); - - $diff = "in object of class <{$expectedClass}>:\n"; - $i = 0; - - foreach($expectedReflection->getProperties() as $expectedAttribute) { - if ($expectedAttribute->isPrivate() || - $expectedAttribute->isProtected()) { - continue; - } - - $actualAttribute = $actualReflection->getProperty( - $expectedAttribute->getName() - ); - $expectedValue = $expectedAttribute->getValue( - $this->expected - ); - $actualValue = $actualAttribute->getValue($this->actual); - - if ($expectedValue !== $actualValue) { - if ($i > 0) { - $diff .= "\n"; - } - - ++$i; - - $expectedType = gettype($expectedValue); - $actualType = gettype($actualValue); - - if ($expectedType !== $actualType) { - $diffObject = new PHPUnit_Framework_ComparisonFailure_Type( - $expectedValue, - $actualValue, - $this->message . 'attribute <' . - $expectedAttribute->getName() . '>: ' - ); - - $diff .= $diffObject->toString(); - } - - elseif (is_object($expectedValue)) { - if (get_class($expectedValue) !== get_class($actualValue)) { - $diffObject = new PHPUnit_Framework_ComparisonFailure_Type( - $expectedValue, - $actualValue, - $this->message . 'attribute <' . - $expectedAttribute->getName() . '>: ' - ); - - $diff .= $diffObject->toString(); - } else { - $diff .= 'attribute <' . - $expectedAttribute->getName() . - '> contains object <' . - get_class($expectedValue) . - '> with different attributes'; - } - } else { - $diffObject = PHPUnit_Framework_ComparisonFailure::diffIdentical( - $expectedValue, - $actualValue, - $this->message . 'attribute <' . - $expectedAttribute->getName() . '>: ' - ); - - $diff .= $diffObject->toString(); - } - } - } - - return $diff; - } - } -} diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint.php b/libs/PHPUnit/PHPUnit/Framework/Constraint.php index 5ba4edb..dcb6ca3 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,122 +50,131 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 { + /** - * Counts the number of constraint elements. + * Evaluates the constraint for parameter $other * - * @return integer - * @since Method available since Release 3.4.0 + * 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 count() + public function evaluate($other, $description = '', $returnResult = FALSE) { - return 1; + $success = FALSE; + + if ($this->matches($other)) { + $success = TRUE; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } } /** - * Creates the appropriate exception for the constraint which can be caught - * by the unit test system. This can be called if a call to evaluate() - * fails. + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. * - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * @return bool */ - public function fail($other, $description, $not = FALSE) + protected function matches($other) { - throw new PHPUnit_Framework_ExpectationFailedException( - $this->failureDescription($other, $description, $not), - NULL - ); + return FALSE; } /** - * @param mixed $other - * @param string $description - * @param boolean $not + * Counts the number of constraint elements. + * + * @return integer + * @since Method available since Release 3.4.0 */ - protected function failureDescription($other, $description, $not) + public function count() { - $failureDescription = $this->customFailureDescription( - $other, $description, $not - ); - - if ($failureDescription === NULL) { - $failureDescription = sprintf( - 'Failed asserting that %s %s.', + return 1; + } - PHPUnit_Util_Type::toString($other), - $this->toString() - ); - } + /** + * 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) + ); - if ($not) { - $failureDescription = self::negate($failureDescription); + $additionalFailureDescription = $this->additionalFailureDescription($other); + if ($additionalFailureDescription) { + $failureDescription .= "\n" . $additionalFailureDescription; } if (!empty($description)) { $failureDescription = $description . "\n" . $failureDescription; } - return $failureDescription; + throw new PHPUnit_Framework_ExpectationFailedException( + $failureDescription, + $comparisonFailure + ); } /** - * @param mixed $other - * @param string $description - * @param boolean $not + * 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 customFailureDescription($other, $description, $not) + protected function additionalFailureDescription($other) { + return ""; } /** - * @param string $string + * 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 */ - public static function negate($string) + protected function failureDescription($other) { - return str_replace( - array( - 'contains ', - 'exists', - 'has ', - 'is ', - 'matches ', - 'starts with ', - 'ends with ', - 'not not ' - ), - array( - 'does not contain ', - 'does not exist', - 'does not have ', - 'is not ', - 'does not match ', - 'starts not with ', - 'ends not with ', - 'not ' - ), - $string - ); + return PHPUnit_Util_Type::export($other) . ' ' . $this->toString(); } - - /** - * 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 - */ - abstract public function evaluate($other); } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/And.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/And.php index fa372a1..d8d0337 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/And.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/And.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -68,15 +69,16 @@ class PHPUnit_Framework_Constraint_And extends PHPUnit_Framework_Constraint protected $lastConstraint = NULL; /** - * @param PHPUnit_Framework_Constraint[] $constraints + * @param PHPUnit_Framework_Constraint[] $constraints + * @throws PHPUnit_Framework_Exception */ public function setConstraints(array $constraints) { $this->constraints = array(); - foreach($constraints as $key => $constraint) { + foreach ($constraints as $key => $constraint) { if (!($constraint instanceof PHPUnit_Framework_Constraint)) { - throw new InvalidArgumentException( + throw new PHPUnit_Framework_Exception( 'All parameters to ' . __CLASS__ . ' must be a constraint object.' ); @@ -87,38 +89,40 @@ public function setConstraints(array $constraints) } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool + * 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) + public function evaluate($other, $description = '', $returnResult = FALSE) { - $this->lastConstraint = NULL; - - foreach($this->constraints as $constraint) { - $this->lastConstraint = $constraint; + $success = TRUE; + $constraint = NULL; - if (!$constraint->evaluate($other)) { - return FALSE; + foreach ($this->constraints as $constraint) { + if (!$constraint->evaluate($other, $description, TRUE)) { + $success = FALSE; + break; } } - return TRUE; - } + if ($returnResult) { + return $success; + } - /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException - */ - public function fail($other, $description, $not = FALSE) - { - $this->lastConstraint->fail($other, $description, $not); + if (!$success) { + $this->fail($other, $description); + } } /** @@ -130,7 +134,7 @@ public function toString() { $text = ''; - foreach($this->constraints as $key => $constraint) { + foreach ($this->constraints as $key => $constraint) { if ($key > 0) { $text .= ' and '; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/ArrayHasKey.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/ArrayHasKey.php index 9903fac..443eb36 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/ArrayHasKey.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/ArrayHasKey.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -54,9 +55,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -82,7 +83,7 @@ public function __construct($key) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return array_key_exists($this->key, $other); } @@ -94,20 +95,20 @@ public function evaluate($other) */ public function toString() { - return 'has the key ' . PHPUnit_Util_Type::toString($this->key); + return 'has the key ' . PHPUnit_Util_Type::export($this->key); } /** - * @param mixed $other - * @param string $description - * @param boolean $not + * 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 customFailureDescription($other, $description, $not) + protected function failureDescription($other) { - return sprintf( - 'Failed asserting that an array %s.', - - $this->toString() - ); + return 'an array ' . $this->toString(); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/Attribute.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Attribute.php index e17cd5b..d47ef72 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/Attribute.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/Attribute.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,67 +50,55 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 +class PHPUnit_Framework_Constraint_Attribute extends PHPUnit_Framework_Constraint_Composite { /** * @var string */ protected $attributeName; - /** - * @var PHPUnit_Framework_Constraint - */ - protected $constraint; - /** * @param PHPUnit_Framework_Constraint $constraint * @param string $attributeName */ public function __construct(PHPUnit_Framework_Constraint $constraint, $attributeName) { + parent::__construct($constraint); + $this->attributeName = $attributeName; - $this->constraint = $constraint; } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool - */ - public function evaluate($other) - { - return $this->constraint->evaluate( - PHPUnit_Framework_Assert::readAttribute( - $other, $this->attributeName - ) - ); - } - - /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException + * 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 fail($other, $description, $not = FALSE) + public function evaluate($other, $description = '', $returnResult = FALSE) { - parent::fail( + return parent::evaluate( PHPUnit_Framework_Assert::readAttribute( $other, $this->attributeName ), $description, - $not + $returnResult ); } @@ -121,29 +110,20 @@ public function fail($other, $description, $not = FALSE) public function toString() { return 'attribute "' . $this->attributeName . '" ' . - $this->constraint->toString(); + $this->innerConstraint->toString(); } /** - * Counts the number of constraint elements. + * Returns the description of the failure * - * @return integer - * @since Method available since Release 3.4.0 - */ - public function count() - { - return count($this->constraint); - } - - /** - * @since Method available since Release 3.4.0 + * 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 customFailureDescription($other, $description, $not) + protected function failureDescription($other) { - return sprintf( - 'Failed asserting that %s.', - - $this->toString() - ); + return $this->toString(); } } diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/Step.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Callback.php similarity index 55% rename from libs/PHPUnit/PHPUnit/Extensions/Story/Step.php rename to libs/PHPUnit/PHPUnit/Framework/Constraint/Callback.php index 2f42ce4..7a3340d 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/Step.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/Callback.php @@ -35,94 +35,82 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister + * @subpackage Framework_Constraint * @author Sebastian Bergmann * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ /** - * A step of a scenario. + * Constraint that evaluates against a specified closure. * * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister + * @subpackage Framework_Constraint * @author Sebastian Bergmann + * @author Timon Rapp * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ -abstract class PHPUnit_Extensions_Story_Step +class PHPUnit_Framework_Constraint_Callback extends PHPUnit_Framework_Constraint { - /** - * @var string - */ - protected $action; + private $callback; /** - * @var array + * @param callable $value + * @throws InvalidArgumentException */ - protected $arguments; + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException( + sprintf( + 'Specified callback <%s> is not callable.', + $this->callbackToString($callback) + ) + ); + } + $this->callback = $callback; + } /** - * Constructor. + * Evaluates the constraint for parameter $value. Returns TRUE if the + * constraint is met, FALSE otherwise. * - * @param array $arguments + * @param mixed $value Value or object to evaluate. + * @return bool */ - public function __construct(array $arguments) + protected function matches($other) { - $this->action = array_shift($arguments); - $this->arguments = $arguments; + return call_user_func($this->callback, $other); } /** - * Returns this step's action. + * Returns a string representation of the constraint. * * @return string */ - public function getAction() + public function toString() { - return $this->action; + return 'is accepted by specified callback'; } - /** - * Returns this step's arguments. - * - * @param boolean $asString - * @return array|string - */ - public function getArguments($asString = FALSE) + private function callbackToString($callback) { - if (!$asString) { - return $this->arguments; - } else { - switch (count($this->arguments)) { - case 0: { - return ''; - } - break; - - case 1: { - return $this->arguments[0]; - } - break; - - default: { - return var_export($this->arguments, TRUE); - } - } + 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]; } - /** - * Returns this step's name. - * - * @return string - */ - abstract public function getName(); } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasAttribute.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasAttribute.php index fd87b4a..5a6c2e5 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasAttribute.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasAttribute.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,9 +53,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -80,7 +81,7 @@ public function __construct($attributeName) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { $class = new ReflectionClass($other); @@ -101,10 +102,23 @@ public function toString() ); } - protected function customFailureDescription($other, $description, $not) + /** + * 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( - 'Failed asserting that class "%s" %s.', $other, $this->toString() + '%sclass "%s" %s', + + is_object($other) ? 'object of ' : '', + is_object($other) ? get_class($other) : $other, + $this->toString() ); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php index 10bc8c0..e9667fc 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,9 +53,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -67,7 +68,7 @@ class PHPUnit_Framework_Constraint_ClassHasStaticAttribute extends PHPUnit_Frame * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { $class = new ReflectionClass($other); diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/Composite.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Composite.php new file mode 100644 index 0000000..a533f29 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Constraint/Count.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Count.php new file mode 100644 index 0000000..e3c9f0c --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Constraint/Exception.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Exception.php new file mode 100644 index 0000000..4158589 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Constraint/ExceptionCode.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/ExceptionCode.php new file mode 100644 index 0000000..5105657 --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/ExceptionMessage.php similarity index 53% rename from libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter.php rename to libs/PHPUnit/PHPUnit/Framework/Constraint/ExceptionMessage.php index ecb3aed..64263dc 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/ResultPrinter.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/ExceptionMessage.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,68 +35,75 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package PHPUnit - * @subpackage Extensions_Story + * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 + * @since File available since Release 3.6.6 */ /** - * Base for Story result printers. + * * * @package PHPUnit - * @subpackage Extensions_Story + * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 + * @since Class available since Release 3.6.6 */ -class PHPUnit_Extensions_Story_ResultPrinter extends PHPUnit_Util_TestDox_ResultPrinter +class PHPUnit_Framework_Constraint_ExceptionMessage extends PHPUnit_Framework_Constraint { /** - * A test ended. - * - * @param PHPUnit_Framework_Test $test - * @param float $time + * @var integer */ - public function endTest(PHPUnit_Framework_Test $test, $time) - { - if ($test instanceof PHPUnit_Extensions_Story_TestCase || - $test instanceof PHPUnit_Extensions_Story_SeleniumTestCase) { - if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { - $this->successful++; - $success = TRUE; - } else { - $success = FALSE; - } + protected $expectedMessage; - $this->onTest( - $this->currentTestMethodPrettified, - $success, - $test->getScenario()->getSteps() - ); - } + /** + * @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 doEndClass() + protected function matches($other) { - $this->endClass($this->testClass); + return strpos($other->getMessage(), $this->expectedMessage) !== FALSE; } /** - * Handler for 'on test' event. + * Returns the description of the failure * - * @param string $name - * @param boolean $success - * @param array $steps + * 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 */ - protected function onTest($name, $success = TRUE, array $steps = array()) + public function toString() { + return 'exception message contains '; } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/FileExists.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/FileExists.php index 51733e4..f1e4908 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/FileExists.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/FileExists.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -51,9 +52,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -66,37 +67,26 @@ class PHPUnit_Framework_Constraint_FileExists extends PHPUnit_Framework_Constrai * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return file_exists($other); } /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException + * 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 */ - public function fail($other, $description, $not = FALSE) + protected function failureDescription($other) { - $failureDescription = sprintf( - 'Failed asserting that file "%s" exists.', - - $other - ); - - if ($not) { - $failureDescription = self::negate($failureDescription); - } - - if (!empty($description)) { - $failureDescription = $description . "\n" . $failureDescription; - } + return sprintf( + 'file "%s" exists', - throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription + $other ); } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/GreaterThan.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/GreaterThan.php index 8ad85c3..dc78aa2 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/GreaterThan.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/GreaterThan.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -50,9 +51,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -78,7 +79,7 @@ public function __construct($value) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return $this->value < $other; } @@ -90,6 +91,6 @@ public function evaluate($other) */ public function toString() { - return 'is greater than ' . PHPUnit_Util_Type::toString($this->value); + return 'is greater than ' . PHPUnit_Util_Type::export($this->value); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsAnything.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsAnything.php index fb8574e..9ace7f0 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsAnything.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsAnything.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,35 +50,33 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool - */ - public function evaluate($other) - { - return TRUE; - } - - /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. + * 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 fail($other, $description, $not = FALSE) + public function evaluate($other, $description = '', $returnResult = FALSE) { + return $returnResult ? TRUE : NULL; } /** diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEmpty.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEmpty.php index 733a2ce..a3bd9f8 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEmpty.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEmpty.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -64,7 +65,7 @@ class PHPUnit_Framework_Constraint_IsEmpty extends PHPUnit_Framework_Constraint * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return empty($other); } @@ -80,23 +81,24 @@ public function toString() } /** - * @param mixed $other - * @param string $description - * @param boolean $not + * 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 customFailureDescription($other, $description, $not) + protected function failureDescription($other) { $type = gettype($other); - if ($type[0] == 'a' || $type[0] == 'o') { - $type = 'an ' . $type; - } else { - $type = 'a ' . $type; - } - return sprintf( - 'Failed asserting that %s is empty.', - $type + '%s %s %s', + + $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a', + $type, + $this->toString() ); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEqual.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEqual.php index 579ddc9..76a8624 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEqual.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsEqual.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,9 @@ * @subpackage Framework_Constraint * @author Kore Nordmann * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -57,9 +58,9 @@ * @subpackage Framework_Constraint * @author Kore Nordmann * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -90,6 +91,11 @@ class PHPUnit_Framework_Constraint_IsEqual extends PHPUnit_Framework_Constraint */ protected $ignoreCase = FALSE; + /** + * @var PHPUnit_Framework_ComparisonFailure + */ + protected $lastFailure; + /** * @param mixed $value * @param float $delta @@ -123,55 +129,51 @@ public function __construct($value, $delta = 0, $maxDepth = 10, $canonicalize = } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool + * 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) + public function evaluate($other, $description = '', $returnResult = FALSE) { - return $this->recursiveComparison($this->value, $other); - } + $comparatorFactory = PHPUnit_Framework_ComparatorFactory::getDefaultInstance(); - /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException - */ - public function fail($other, $description, $not = FALSE) - { - $failureDescription = $this->failureDescription( - $other, - $description, - $not - ); + try { + $comparator = $comparatorFactory->getComparatorFor( + $other, $this->value + ); - if (!$not) { - if ($this->value instanceof DOMDocument) { - $value = $this->domToText($this->value); - } else { - $value = $this->value; - } + $comparator->assertEquals( + $this->value, + $other, + $this->delta, + $this->canonicalize, + $this->ignoreCase + ); + } - if ($other instanceof DOMDocument) { - $other = $this->domToText($other); + catch (PHPUnit_Framework_ComparisonFailure $f) { + if ($returnResult) { + return FALSE; } throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription, - PHPUnit_Framework_ComparisonFailure::diffEqual($value, $other), - $description - ); - } else { - throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription, - NULL + trim($description . "\n" . $f->getMessage()), + $f ); } + + return TRUE; } /** @@ -205,182 +207,9 @@ public function toString() return sprintf( 'is equal to %s%s', - PHPUnit_Util_Type::toString($this->value), + PHPUnit_Util_Type::export($this->value), $delta ); } } - - /** - * Perform the actual recursive comparision of two values - * - * @param mixed $a First value - * @param mixed $b Second value - * @param int $depth Depth - * @return bool - */ - protected function recursiveComparison($a, $b, $depth = 0) - { - if ($a === $b) { - return TRUE; - } - - if ($depth >= $this->maxDepth) { - return TRUE; - } - - if (is_numeric($a) XOR is_numeric($b)) { - return FALSE; - } - - if (is_array($a) XOR is_array($b)) { - return FALSE; - } - - if (is_object($a) XOR is_object($b)) { - return FALSE; - } - - if ($a instanceof SplObjectStorage XOR $b instanceof SplObjectStorage) { - return FALSE; - } - - if ($a instanceof SplObjectStorage) { - foreach ($a as $object) { - if (!$b->contains($object)) { - return FALSE; - } - } - - foreach ($b as $object) { - if (!$a->contains($object)) { - return FALSE; - } - } - - return TRUE; - } - - if ($a instanceof DOMDocument || $b instanceof DOMDocument) { - if (!$a instanceof DOMDocument) { - $_a = new DOMDocument; - $_a->preserveWhiteSpace = FALSE; - $_a->loadXML($a); - $a = $_a; - unset($_a); - } - - if (!$b instanceof DOMDocument) { - $_b = new DOMDocument; - $_b->preserveWhiteSpace = FALSE; - $_b->loadXML($b); - $b = $_b; - unset($_b); - } - - return $a->C14N() == $b->C14N(); - } - - if (is_object($a) && is_object($b) && - (get_class($a) !== get_class($b))) { - return FALSE; - } - - // Normal comparision for scalar values. - if ((!is_array($a) && !is_object($a)) || - (!is_array($b) && !is_object($b))) { - if (is_numeric($a) && is_numeric($b)) { - // Optionally apply delta on numeric values. - return $this->numericComparison($a, $b); - } - - if (is_string($a) && is_string($b)) { - if ($this->canonicalize && PHP_EOL != "\n") { - $a = str_replace(PHP_EOL, "\n", $a); - $b = str_replace(PHP_EOL, "\n", $b); - } - - if ($this->ignoreCase) { - $a = strtolower($a); - $b = strtolower($b); - } - } - - return ($a == $b); - } - - if (is_object($a)) { - $isMock = $a instanceof PHPUnit_Framework_MockObject_MockObject; - $a = (array)$a; - $b = (array)$b; - - if ($isMock) { - unset($a["\0*\0invocationMocker"]); - - if (isset($b["\0*\0invocationMocker"])) { - unset($b["\0*\0invocationMocker"]); - } - } - } - - if ($this->canonicalize) { - sort($a); - sort($b); - } - - $keysInB = array_flip(array_keys($b)); - - foreach ($a as $key => $v) { - if (!isset($keysInB[$key])) { - // Abort on missing key in $b. - return FALSE; - } - - if (!$this->recursiveComparison($a[$key], $b[$key], $depth + 1)) { - // FALSE, if child comparision fails. - return FALSE; - } - - // Unset key to check whether all keys of b are compared. - unset($b[$key]); - } - - if (count($b)) { - // There is something in $b, that is missing in $a. - return FALSE; - } - - return TRUE; - } - - /** - * Compares two numeric values - use delta if applicable. - * - * @param mixed $a - * @param mixed $b - * @return bool - */ - protected function numericComparison($a, $b) - { - if ($this->delta === FALSE) { - return ($a == $b); - } else { - return (abs($a - $b) <= $this->delta); - } - } - - /** - * 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/libs/PHPUnit/PHPUnit/Framework/Constraint/IsFalse.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsFalse.php index 156acd8..d232008 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsFalse.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsFalse.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -64,7 +65,7 @@ class PHPUnit_Framework_Constraint_IsFalse extends PHPUnit_Framework_Constraint * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return $other === FALSE; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsIdentical.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsIdentical.php index 6c7beec..604258b 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsIdentical.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsIdentical.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -57,14 +58,19 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -79,47 +85,73 @@ public function __construct($value) } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * 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. - * @return bool + * @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) + public function evaluate($other, $description = '', $returnResult = FALSE) { - return $this->value === $other; + 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); + } } /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException + * 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 */ - public function fail($other, $description, $not = FALSE) + protected function failureDescription($other) { - $failureDescription = $this->failureDescription( - $other, - $description, - $not - ); + if (is_object($this->value) && is_object($other)) { + return 'two variables reference the same object'; + } - if (!$not) { - throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription, - PHPUnit_Framework_ComparisonFailure::diffIdentical( - $this->value, $other - ), - $description - ); - } else { - throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription, - NULL - ); + if (is_string($this->value) && is_string($other)) { + return 'two strings are identical'; } + + return parent::failureDescription($other); } /** @@ -134,7 +166,7 @@ public function toString() get_class($this->value) . '"'; } else { return 'is identical to ' . - PHPUnit_Util_Type::toString($this->value); + PHPUnit_Util_Type::export($this->value); } } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsInstanceOf.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsInstanceOf.php index 95cfb1c..d590f12 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsInstanceOf.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsInstanceOf.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,9 +53,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -80,35 +81,27 @@ public function __construct($className) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return ($other instanceof $this->className); } /** - * Creates the appropriate exception for the constraint which can be caught - * by the unit test system. This can be called if a call to evaluate() - * fails. + * Returns the description of the failure * - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException + * 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 */ - public function fail($other, $description, $not = FALSE) + protected function failureDescription($other) { - throw new PHPUnit_Framework_ExpectationFailedException( - sprintf( - '%sFailed asserting that %s is %san instance of class "%s".', + return sprintf( + '%s is an instance of class "%s"', - !empty($description) ? $description . "\n" : '', - PHPUnit_Util_Type::toString($other, TRUE), - $not ? 'not ' : '', - $this->className - ), - NULL + PHPUnit_Util_Type::shortenedExport($other), + $this->className ); } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsNull.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsNull.php index c179525..784f1ad 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsNull.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsNull.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -64,7 +65,7 @@ class PHPUnit_Framework_Constraint_IsNull extends PHPUnit_Framework_Constraint * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return $other === NULL; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsTrue.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsTrue.php index fede126..262da8e 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsTrue.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsTrue.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -64,7 +65,7 @@ class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return $other === TRUE; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsType.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsType.php index 38d8af7..0fad628 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/IsType.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/IsType.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,9 +53,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -70,6 +71,7 @@ class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint const TYPE_RESOURCE = 'resource'; const TYPE_STRING = 'string'; const TYPE_SCALAR = 'scalar'; + const TYPE_CALLABLE = 'callable'; /** * @var array @@ -86,7 +88,8 @@ class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint 'object' => TRUE, 'resource' => TRUE, 'string' => TRUE, - 'scalar' => TRUE + 'scalar' => TRUE, + 'callable' => TRUE ); /** @@ -96,12 +99,12 @@ class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint /** * @param string $type - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function __construct($type) { if (!isset($this->types[$type])) { - throw new InvalidArgumentException( + throw new PHPUnit_Framework_Exception( sprintf( 'Type specified for PHPUnit_Framework_Constraint_IsType <%s> ' . 'is not a valid type.', @@ -120,7 +123,7 @@ public function __construct($type) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { switch ($this->type) { case 'numeric': { @@ -164,6 +167,10 @@ public function evaluate($other) case 'scalar': { return is_scalar($other); } + + case 'callable': { + return is_callable($other); + } } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/JsonMatches.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/JsonMatches.php new file mode 100644 index 0000000..01bb70b --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php new file mode 100644 index 0000000..5a984db --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Framework/Constraint/LessThan.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/LessThan.php index c560285..27eaa1f 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/LessThan.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/LessThan.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -50,9 +51,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -78,7 +79,7 @@ public function __construct($value) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return $this->value > $other; } @@ -90,6 +91,6 @@ public function evaluate($other) */ public function toString() { - return 'is less than ' . PHPUnit_Util_Type::toString($this->value); + return 'is less than ' . PHPUnit_Util_Type::export($this->value); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/Not.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Not.php index 59bab41..c839487 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/Not.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/Not.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -76,32 +77,93 @@ public function __construct($constraint) } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * @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. * - * @param mixed $other Value or object to evaluate. - * @return bool + * 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) + public function evaluate($other, $description = '', $returnResult = FALSE) { - return !$this->constraint->evaluate($other); + $success = !$this->constraint->evaluate($other, $description, TRUE); + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } } /** - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException + * 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 */ - public function fail($other, $description, $not = FALSE) + protected function failureDescription($other) { - if (count($this->constraint) == 1 || - $this->constraint instanceof PHPUnit_Framework_Constraint_Attribute) { - $this->constraint->fail($other, $description, TRUE); - } else { - parent::fail($other, $description, !$not); + 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) + ); + } } } @@ -121,7 +183,7 @@ public function toString() break; default: { - return PHPUnit_Framework_Constraint::negate( + return self::negate( $this->constraint->toString() ); } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php index e63fe5d..872c3b3 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,9 +53,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -67,18 +68,10 @@ class PHPUnit_Framework_Constraint_ObjectHasAttribute extends PHPUnit_Framework_ * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { $object = new ReflectionObject($other); return $object->hasProperty($this->attributeName); } - - protected function customFailureDescription($other, $description, $not) - { - return sprintf( - 'Failed asserting that object of class "%s" %s.', - get_class($other), $this->toString() - ); - } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/Or.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Or.php index 356571b..6ee3842 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/Or.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/Or.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -69,7 +70,7 @@ public function setConstraints(array $constraints) { $this->constraints = array(); - foreach($constraints as $key => $constraint) { + foreach ($constraints as $key => $constraint) { if (!($constraint instanceof PHPUnit_Framework_Constraint)) { $constraint = new PHPUnit_Framework_Constraint_IsEqual( $constraint @@ -81,21 +82,40 @@ public function setConstraints(array $constraints) } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool + * 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) + public function evaluate($other, $description = '', $returnResult = FALSE) { - foreach($this->constraints as $constraint) { - if ($constraint->evaluate($other)) { - return TRUE; + $success = FALSE; + $constraint = NULL; + + foreach ($this->constraints as $constraint) { + if ($constraint->evaluate($other, $description, TRUE)) { + $success = TRUE; + break; } } - return FALSE; + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } } /** @@ -107,7 +127,7 @@ public function toString() { $text = ''; - foreach($this->constraints as $key => $constraint) { + foreach ($this->constraints as $key => $constraint) { if ($key > 0) { $text .= ' or '; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/PCREMatch.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/PCREMatch.php index c0e0c79..7f00e4a 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/PCREMatch.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/PCREMatch.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -55,9 +56,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -83,7 +84,7 @@ public function __construct($pattern) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return preg_match($this->pattern, $other) > 0; } diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/When.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/SameSize.php similarity index 66% rename from libs/PHPUnit/PHPUnit/Extensions/Story/When.php rename to libs/PHPUnit/PHPUnit/Framework/Constraint/SameSize.php index 66d13ac..2f4d8ab 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/When.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/SameSize.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,37 +35,39 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister + * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 + * @since File available since Release 3.6.0 */ /** - * A "When" step. + * * * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister + * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 + * @since Class available since Release 3.6.0 */ -class PHPUnit_Extensions_Story_When extends PHPUnit_Extensions_Story_Step +class PHPUnit_Framework_Constraint_SameSize extends PHPUnit_Framework_Constraint_Count { /** - * Returns this step's name. - * - * @return string + * @var integer + */ + protected $expectedCount; + + /** + * @param integer $expected */ - public function getName() + public function __construct($expected) { - return 'When'; + $this->expectedCount = $this->getCountOf($expected); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringContains.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringContains.php index af19a55..ab4eb41 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringContains.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringContains.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -55,9 +56,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -90,7 +91,7 @@ public function __construct($string, $ignoreCase = FALSE) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { if ($this->ignoreCase) { return stripos($other, $this->string) !== FALSE; diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringEndsWith.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringEndsWith.php index 0e87363..cd145e6 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringEndsWith.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringEndsWith.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -50,9 +51,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -78,7 +79,7 @@ public function __construct($suffix) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return substr($other, 0 - strlen($this->suffix)) == $this->suffix; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringMatches.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringMatches.php index 48a6b58..9d21664 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringMatches.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringMatches.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -67,8 +68,37 @@ class PHPUnit_Framework_Constraint_StringMatches extends PHPUnit_Framework_Const */ public function __construct($string) { - $this->pattern = preg_quote(preg_replace('/\r\n/', "\n", $string), '/'); - $this->pattern = str_replace( + $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', @@ -95,46 +125,10 @@ public function __construct($string) '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', '.' ), - $this->pattern + preg_quote($string, '/') ); - - $this->pattern = '/^' . $this->pattern . '$/s'; - $this->string = $string; + return '/^' . $string . '$/s'; } - /** - * Creates the appropriate exception for the constraint which can be caught - * by the unit test system. This can be called if a call to evaluate() - * fails. - * - * @param mixed $other The value passed to evaluate() which failed the - * constraint check. - * @param string $description A string with extra description of what was - * going on while the evaluation failed. - * @param boolean $not Flag to indicate negation. - * @throws PHPUnit_Framework_ExpectationFailedException - */ - public function fail($other, $description, $not = FALSE) - { - $failureDescription = $this->failureDescription( - $other, - $description, - $not - ); - - if (!$not) { - throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription, - PHPUnit_Framework_ComparisonFailure::diffEqual( - $this->string, $other - ), - $description - ); - } else { - throw new PHPUnit_Framework_ExpectationFailedException( - $failureDescription, - NULL - ); - } - } } + diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringStartsWith.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringStartsWith.php index 02339ce..6051a05 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/StringStartsWith.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/StringStartsWith.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -50,9 +51,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -78,7 +79,7 @@ public function __construct($prefix) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { return strpos($other, $this->prefix) === 0; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContains.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContains.php index 6dbf7da..46f1bed 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContains.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContains.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -50,25 +51,37 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 mixed $value + * @param boolean $value + * @param mixed $checkForObjectIdentity + * @throws PHPUnit_Framework_Exception */ - public function __construct($value) + public function __construct($value, $checkForObjectIdentity = TRUE) { - $this->value = $value; + if (!is_bool($checkForObjectIdentity)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean'); + } + + $this->checkForObjectIdentity = $checkForObjectIdentity; + $this->value = $value; } /** @@ -78,7 +91,7 @@ public function __construct($value) * @param mixed $other Value or object to evaluate. * @return bool */ - public function evaluate($other) + protected function matches($other) { if ($other instanceof SplObjectStorage) { return $other->contains($this->value); @@ -86,7 +99,10 @@ public function evaluate($other) if (is_object($this->value)) { foreach ($other as $element) { - if ($element === $this->value) { + if (($this->checkForObjectIdentity && + $element === $this->value) || + (!$this->checkForObjectIdentity && + $element == $this->value)) { return TRUE; } } @@ -111,14 +127,23 @@ public function toString() if (is_string($this->value) && strpos($this->value, "\n") !== FALSE) { return 'contains "' . $this->value . '"'; } else { - return 'contains ' . PHPUnit_Util_Type::toString($this->value); + return 'contains ' . PHPUnit_Util_Type::export($this->value); } } - protected function customFailureDescription($other, $description, $not) + /** + * 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( - 'Failed asserting that an %s %s.', + 'an %s %s', is_array($other) ? 'array' : 'iterator', $this->toString() diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php index 6cb299a..bab400c 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -50,9 +51,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -86,21 +87,40 @@ public function __construct($type, $isNativeType = TRUE) } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool + * 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) + public function evaluate($other, $description = '', $returnResult = FALSE) { + $success = TRUE; + $constraint = NULL; + foreach ($other as $item) { - if (!$this->constraint->evaluate($item)) { - return FALSE; + if (!$this->constraint->evaluate($item, '', TRUE)) { + $success = FALSE; + break; } } - return TRUE; + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } } /** diff --git a/libs/PHPUnit/PHPUnit/Framework/Constraint/Xor.php b/libs/PHPUnit/PHPUnit/Framework/Constraint/Xor.php index 63e6d02..cc9c067 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Constraint/Xor.php +++ b/libs/PHPUnit/PHPUnit/Framework/Constraint/Xor.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +50,9 @@ * @package PHPUnit * @subpackage Framework_Constraint * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ @@ -69,7 +70,7 @@ public function setConstraints(array $constraints) { $this->constraints = array(); - foreach($constraints as $key => $constraint) { + foreach ($constraints as $key => $constraint) { if (!($constraint instanceof PHPUnit_Framework_Constraint)) { $constraint = new PHPUnit_Framework_Constraint_IsEqual( $constraint @@ -81,28 +82,45 @@ public function setConstraints(array $constraints) } /** - * Evaluates the constraint for parameter $other. Returns TRUE if the - * constraint is met, FALSE otherwise. + * Evaluates the constraint for parameter $other * - * @param mixed $other Value or object to evaluate. - * @return bool + * 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) + public function evaluate($other, $description = '', $returnResult = FALSE) { - $result = FALSE; + $success = TRUE; + $lastResult = NULL; + $constraint = NULL; - foreach($this->constraints as $constraint) { - if ($constraint->evaluate($other)) { - if ( $result ) - { - return FALSE; - } + foreach ($this->constraints as $constraint) { + $result = $constraint->evaluate($other, $description, TRUE); - $result = TRUE; + if ($result === $lastResult) { + $success = FALSE; + break; } + + $lastResult = $result; } - return $result; + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } } /** @@ -114,7 +132,7 @@ public function toString() { $text = ''; - foreach($this->constraints as $key => $constraint) { + foreach ($this->constraints as $key => $constraint) { if ($key > 0) { $text .= ' xor '; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Error.php b/libs/PHPUnit/PHPUnit/Framework/Error.php index 3beeee6..04f632a 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Error.php +++ b/libs/PHPUnit/PHPUnit/Framework/Error.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -60,18 +59,17 @@ class PHPUnit_Framework_Error extends Exception /** * Constructor. * - * @param string $message - * @param integer $code - * @param string $file - * @param integer $line - * @param array $trace + * @param string $message + * @param integer $code + * @param string $file + * @param integer $line + * @param Exception $previous */ - public function __construct($message, $code, $file, $line, $trace) + public function __construct($message, $code, $file, $line, Exception $previous = NULL) { - parent::__construct($message, $code); + parent::__construct($message, $code, $previous); $this->file = $file; $this->line = $line; - $this->trace = $trace; } } diff --git a/libs/PHPUnit/PHPUnit/Extensions/Story/Then.php b/libs/PHPUnit/PHPUnit/Framework/Error/Deprecated.php similarity index 71% rename from libs/PHPUnit/PHPUnit/Extensions/Story/Then.php rename to libs/PHPUnit/PHPUnit/Framework/Error/Deprecated.php index dd746ad..4252668 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/Then.php +++ b/libs/PHPUnit/PHPUnit/Framework/Error/Deprecated.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,37 +35,31 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister + * @subpackage Framework_Error * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ /** - * A "Then" step. + * Wrapper for PHP deprecated errors. + * You can disable deprecated-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Deprecated::$enabled = FALSE; + * * * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister + * @subpackage Framework_Error * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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_Story_Then extends PHPUnit_Extensions_Story_Step +class PHPUnit_Framework_Error_Deprecated extends PHPUnit_Framework_Error { - /** - * Returns this step's name. - * - * @return string - */ - public function getName() - { - return 'Then'; - } + public static $enabled = TRUE; } diff --git a/libs/PHPUnit/PHPUnit/Framework/Error/Notice.php b/libs/PHPUnit/PHPUnit/Framework/Error/Notice.php index 25f5b65..c640692 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Error/Notice.php +++ b/libs/PHPUnit/PHPUnit/Framework/Error/Notice.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework_Error * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -54,9 +54,8 @@ * @package PHPUnit * @subpackage Framework_Error * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/Error/Warning.php b/libs/PHPUnit/PHPUnit/Framework/Error/Warning.php index 4482565..d6064fc 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Error/Warning.php +++ b/libs/PHPUnit/PHPUnit/Framework/Error/Warning.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework_Error * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -54,9 +54,8 @@ * @package PHPUnit * @subpackage Framework_Error * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/Exception.php b/libs/PHPUnit/PHPUnit/Framework/Exception.php index 181ecc7..2c0b398 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Exception.php +++ b/libs/PHPUnit/PHPUnit/Framework/Exception.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/ExpectationFailedException.php b/libs/PHPUnit/PHPUnit/Framework/ExpectationFailedException.php index 4b250b0..ba08851 100644 --- a/libs/PHPUnit/PHPUnit/Framework/ExpectationFailedException.php +++ b/libs/PHPUnit/PHPUnit/Framework/ExpectationFailedException.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -53,9 +53,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -66,27 +65,11 @@ class PHPUnit_Framework_ExpectationFailedException extends PHPUnit_Framework_Ass */ protected $comparisonFailure; - /** - * @var string - */ - protected $description; - - /** - * @var string - */ - protected $customMessage; - - public function __construct($description, PHPUnit_Framework_ComparisonFailure $comparisonFailure = NULL, $message = '') + public function __construct($message, PHPUnit_Framework_ComparisonFailure $comparisonFailure = NULL, Exception $previous = NULL) { - $this->description = $description; $this->comparisonFailure = $comparisonFailure; - $this->customMessage = $message; - if (!empty($message)) { - $description .= "\n" . $message; - } - - parent::__construct($description); + parent::__construct($message, 0, $previous); } /** @@ -96,29 +79,4 @@ public function getComparisonFailure() { return $this->comparisonFailure; } - - /** - * @return string - */ - public function getDescription() - { - return $this->description; - } - - /** - * @return string - */ - public function getCustomMessage() - { - return $this->customMessage; - } - - /** - * @param string $customMessage - * @since Method available since Release 3.4.0 - */ - public function setCustomMessage($customMessage) - { - $this->customMessage = $customMessage; - } } diff --git a/libs/PHPUnit/PHPUnit/Framework/IncompleteTest.php b/libs/PHPUnit/PHPUnit/Framework/IncompleteTest.php index 4d37593..12ac588 100644 --- a/libs/PHPUnit/PHPUnit/Framework/IncompleteTest.php +++ b/libs/PHPUnit/PHPUnit/Framework/IncompleteTest.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/IncompleteTestError.php b/libs/PHPUnit/PHPUnit/Framework/IncompleteTestError.php index 084e89c..4ae6d5f 100644 --- a/libs/PHPUnit/PHPUnit/Framework/IncompleteTestError.php +++ b/libs/PHPUnit/PHPUnit/Framework/IncompleteTestError.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/MockObject/Autoload.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Autoload.php new file mode 100644 index 0000000..296a1ac --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Autoload.php.in b/libs/PHPUnit/PHPUnit/Framework/MockObject/Autoload.php.in new file mode 100644 index 0000000..fe019c0 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Identity.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Identity.php new file mode 100644 index 0000000..83177f4 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php new file mode 100644 index 0000000..7fc4fed --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Match.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Match.php new file mode 100644 index 0000000..ce998cb --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php new file mode 100644 index 0000000..6346842 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Namespace.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Namespace.php new file mode 100644 index 0000000..e14a9a7 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php new file mode 100644 index 0000000..c907456 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Stub.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Builder/Stub.php new file mode 100644 index 0000000..25c31c9 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator.php new file mode 100644 index 0000000..45f0d22 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_class.tpl.dist new file mode 100644 index 0000000..5e22d7b --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_clone.tpl.dist new file mode 100644 index 0000000..e8d7ad0 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_object_method.tpl.dist new file mode 100644 index 0000000..e2f55d9 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/mocked_static_method.tpl.dist new file mode 100644 index 0000000..06df070 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist new file mode 100644 index 0000000..48b4bbf --- /dev/null +++ b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/trait_class.tpl.dist @@ -0,0 +1,4 @@ +class {class_name} +{ + use {trait_name}; +} diff --git a/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/unmocked_clone.tpl.dist new file mode 100644 index 0000000..df829bd --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_class.tpl.dist new file mode 100644 index 0000000..05ad1d7 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist new file mode 100644 index 0000000..bb16e76 --- /dev/null +++ b/libs/PHPUnit/PHPUnit/Framework/MockObject/Generator/wsdl_method.tpl.dist @@ -0,0 +1,4 @@ + + public function {method_name}({arguments}) + { + } diff --git a/libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation.php new file mode 100644 index 0000000..70666ac --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation/Object.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation/Object.php new file mode 100644 index 0000000..70754c5 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation/Static.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Invocation/Static.php new file mode 100644 index 0000000..4155788 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/InvocationMocker.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/InvocationMocker.php new file mode 100644 index 0000000..4a3c04a --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Invokable.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Invokable.php new file mode 100644 index 0000000..13d3439 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher.php new file mode 100644 index 0000000..13cc2a0 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Type.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php similarity index 57% rename from libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Type.php rename to libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php index 12b000f..cf86c9e 100644 --- a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Type.php +++ b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2010-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,40 +34,39 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.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 + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 */ /** - * Thrown when an assertion for type equality failed. + * Invocation matcher which checks if a method has been invoked zero or more + * times. This matcher will always match. * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.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_ComparisonFailure_Type extends PHPUnit_Framework_ComparisonFailure +class PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder { /** - * Returns a string describing the type difference between the expected - * and the actual value. + * @return string */ public function toString() { - return sprintf( - '%s does not match expected type "%s".', + return 'invoked zero or more times'; + } - PHPUnit_Util_Type::toString($this->actual), - gettype($this->expected) - ); + /** + */ + public function verify() + { } } diff --git a/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php new file mode 100644 index 0000000..5c20f41 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/Invocation.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/Invocation.php new file mode 100644 index 0000000..8ffcdcf --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php new file mode 100644 index 0000000..3026182 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php new file mode 100644 index 0000000..2494110 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php new file mode 100644 index 0000000..3c40018 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php new file mode 100644 index 0000000..ab6f753 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/MethodName.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/MethodName.php new file mode 100644 index 0000000..58b151e --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/Parameters.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/Parameters.php new file mode 100644 index 0000000..c2700a7 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php new file mode 100644 index 0000000..1b3203f --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/MockBuilder.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/MockBuilder.php new file mode 100644 index 0000000..02a2675 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/MockObject.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/MockObject.php new file mode 100644 index 0000000..20db63b --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub.php new file mode 100644 index 0000000..db3aebc --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php new file mode 100644 index 0000000..eedd6a1 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/Exception.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/Exception.php new file mode 100644 index 0000000..571f9f6 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php new file mode 100644 index 0000000..a6c0447 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Scalar.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/Return.php similarity index 55% rename from libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Scalar.php rename to libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/Return.php index 7e981ac..bcbfd5e 100644 --- a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/Scalar.php +++ b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/Return.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2010-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,41 +34,45 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.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 + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 */ /** - * Thrown when an assertion for scalar equality failed. + * Stubs a method by returning a user-defined value. * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.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_ComparisonFailure_Scalar extends PHPUnit_Framework_ComparisonFailure +class PHPUnit_Framework_MockObject_Stub_Return implements PHPUnit_Framework_MockObject_Stub { - /** - * Returns a string describing the difference between the expected and the - * actual scalar value. - */ + 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( - 'Failed asserting that %s %s %s.', + 'return user-specified value %s', - PHPUnit_Util_Type::toString($this->actual), - $this->identical ? 'is identical to' : 'matches expected', - PHPUnit_Util_Type::toString($this->expected) + PHPUnit_Util_Type::toString($this->value) ); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/String.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php similarity index 52% rename from libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/String.php rename to libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php index 67361ae..0060f81 100644 --- a/libs/PHPUnit/PHPUnit/Framework/ComparisonFailure/String.php +++ b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2010-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,49 +34,45 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.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 + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 */ /** - * Thrown when an assertion for string equality failed. + * Stubs a method by returning an argument that was passed to the mocked method. * - * @package PHPUnit - * @subpackage Framework_ComparisonFailure - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.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_ComparisonFailure_String extends PHPUnit_Framework_ComparisonFailure +class PHPUnit_Framework_MockObject_Stub_ReturnArgument extends PHPUnit_Framework_MockObject_Stub_Return { - /** - * Returns a string describing the difference between - * the expected and the actual string value. - */ - public function toString() - { - $expected = (string)$this->expected; - $actual = (string)$this->actual; - $diff = PHPUnit_Util_Diff::diff($expected, $actual); + protected $argumentIndex; - if ($diff === FALSE) { - $diff = ''; - } + public function __construct($argumentIndex) + { + $this->argumentIndex = $argumentIndex; + } - if (!empty($this->message)) { - $buffer = $this->message . "\n"; + public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation) + { + if (isset($invocation->parameters[$this->argumentIndex])) { + return $invocation->parameters[$this->argumentIndex]; } else { - $buffer = ''; + return NULL; } + } - return trim($buffer . $diff); + public function toString() + { + return sprintf('return argument #%d', $this->argumentIndex); } } diff --git a/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php new file mode 100644 index 0000000..c98c4f7 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php new file mode 100644 index 0000000..e686c1b --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php new file mode 100644 index 0000000..5280b33 --- /dev/null +++ b/libs/PHPUnit/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/libs/PHPUnit/PHPUnit/Extensions/Story/Given.php b/libs/PHPUnit/PHPUnit/Framework/MockObject/Verifiable.php similarity index 57% rename from libs/PHPUnit/PHPUnit/Extensions/Story/Given.php rename to libs/PHPUnit/PHPUnit/Framework/MockObject/Verifiable.php index b9c2623..77aa492 100644 --- a/libs/PHPUnit/PHPUnit/Extensions/Story/Given.php +++ b/libs/PHPUnit/PHPUnit/Framework/MockObject/Verifiable.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2010-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,38 +34,32 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.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 + * @link http://github.com/sebastianbergmann/phpunit-mock-objects + * @since File available since Release 1.0.0 */ /** - * A "Given" step. + * Interface for classes which must verify a given expectation. * - * @package PHPUnit - * @subpackage Extensions_Story - * @author Mattis Stordalen Flister - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.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 Interface available since Release 1.0.0 */ -class PHPUnit_Extensions_Story_Given extends PHPUnit_Extensions_Story_Step +interface PHPUnit_Framework_MockObject_Verifiable { /** - * Returns this step's name. + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. * - * @return string + * @throws PHPUnit_Framework_ExpectationFailedException */ - public function getName() - { - return 'Given'; - } + public function verify(); } diff --git a/libs/PHPUnit/PHPUnit/Runner/TestCollector.php b/libs/PHPUnit/PHPUnit/Framework/OutputError.php similarity index 72% rename from libs/PHPUnit/PHPUnit/Runner/TestCollector.php rename to libs/PHPUnit/PHPUnit/Framework/OutputError.php index a9a99a5..507363e 100644 --- a/libs/PHPUnit/PHPUnit/Runner/TestCollector.php +++ b/libs/PHPUnit/PHPUnit/Framework/OutputError.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,31 +35,26 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package PHPUnit - * @subpackage Runner + * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 + * @since File available since Release 3.6.0 */ /** - * Collects Test class names to be presented - * by the TestSelector. + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a test that printed output. * * @package PHPUnit - * @subpackage Runner + * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 + * @since Class available since Release 3.6.0 */ -interface PHPUnit_Runner_TestCollector +class PHPUnit_Framework_OutputError extends PHPUnit_Framework_AssertionFailedError { - /** - * @return array - */ - public function collectTests(); } diff --git a/libs/PHPUnit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist b/libs/PHPUnit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist index 2ddd4fa..369e3ed 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist +++ b/libs/PHPUnit/PHPUnit/Framework/Process/TestCaseMethod.tpl.dist @@ -1,6 +1,6 @@ collectRawCodeCoverageInformation({collectCodeCoverageInformation}); + + if ({collectCodeCoverageInformation}) { + $result->setCodeCoverage(new PHP_CodeCoverage); + } + $result->strictMode({strict}); $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); @@ -45,4 +49,3 @@ if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { __phpunit_run_isolated_test(); ob_end_clean(); -?> diff --git a/libs/PHPUnit/PHPUnit/Framework/SelfDescribing.php b/libs/PHPUnit/PHPUnit/Framework/SelfDescribing.php index 4a21f23..3801405 100644 --- a/libs/PHPUnit/PHPUnit/Framework/SelfDescribing.php +++ b/libs/PHPUnit/PHPUnit/Framework/SelfDescribing.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/SkippedTest.php b/libs/PHPUnit/PHPUnit/Framework/SkippedTest.php index 503a0a4..5b3eae9 100644 --- a/libs/PHPUnit/PHPUnit/Framework/SkippedTest.php +++ b/libs/PHPUnit/PHPUnit/Framework/SkippedTest.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/SkippedTestError.php b/libs/PHPUnit/PHPUnit/Framework/SkippedTestError.php index 5d22e50..94c0022 100644 --- a/libs/PHPUnit/PHPUnit/Framework/SkippedTestError.php +++ b/libs/PHPUnit/PHPUnit/Framework/SkippedTestError.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/SkippedTestSuiteError.php b/libs/PHPUnit/PHPUnit/Framework/SkippedTestSuiteError.php index 9f6b5c7..95d4dde 100644 --- a/libs/PHPUnit/PHPUnit/Framework/SkippedTestSuiteError.php +++ b/libs/PHPUnit/PHPUnit/Framework/SkippedTestSuiteError.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/SyntheticError.php b/libs/PHPUnit/PHPUnit/Framework/SyntheticError.php index 7a3e2d3..0a6c618 100644 --- a/libs/PHPUnit/PHPUnit/Framework/SyntheticError.php +++ b/libs/PHPUnit/PHPUnit/Framework/SyntheticError.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/Test.php b/libs/PHPUnit/PHPUnit/Framework/Test.php index 63ac4e1..c873748 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Test.php +++ b/libs/PHPUnit/PHPUnit/Framework/Test.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/TestCase.php b/libs/PHPUnit/PHPUnit/Framework/TestCase.php index eeaf29d..a432a51 100644 --- a/libs/PHPUnit/PHPUnit/Framework/TestCase.php +++ b/libs/PHPUnit/PHPUnit/Framework/TestCase.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,14 +37,12 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'Text/Template.php'; - /** * A TestCase defines the fixture to run multiple tests. * @@ -92,9 +90,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -105,12 +102,12 @@ abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert imple * Overwrite this attribute in a child class of TestCase. * Setting this attribute in setUp() has no effect! * - * @var boolean + * @var boolean */ protected $backupGlobals = NULL; /** - * @var array + * @var array */ protected $backupGlobalsBlacklist = array(); @@ -119,19 +116,19 @@ abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert imple * Overwrite this attribute in a child class of TestCase. * Setting this attribute in setUp() has no effect! * - * @var boolean + * @var boolean */ protected $backupStaticAttributes = NULL; /** - * @var array + * @var array */ protected $backupStaticAttributesBlacklist = array(); /** * Whether or not this test is to be run in a separate PHP process. * - * @var boolean + * @var boolean */ protected $runTestInSeparateProcess = NULL; @@ -139,119 +136,114 @@ abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert imple * Whether or not this test should preserve the global state when * running in a separate PHP process. * - * @var boolean + * @var boolean */ protected $preserveGlobalState = TRUE; /** * Whether or not this test is running in a separate PHP process. * - * @var boolean + * @var boolean */ private $inIsolation = FALSE; /** - * @var array + * @var array */ private $data = array(); /** - * @var string + * @var string */ private $dataName = ''; /** - * @var boolean + * @var boolean */ private $useErrorHandler = NULL; /** - * @var boolean + * @var boolean */ private $useOutputBuffering = NULL; /** * The name of the expected Exception. * - * @var mixed + * @var mixed */ private $expectedException = NULL; /** * The message of the expected Exception. * - * @var string + * @var string */ private $expectedExceptionMessage = ''; /** * The code of the expected Exception. * - * @var integer + * @var integer */ private $expectedExceptionCode; /** - * The stack trace to where the expected exception was set. + * The required preconditions for a test. * - * @var array + * @var array */ - private $expectedExceptionTrace = array(); + private $required = array( + 'PHP' => NULL, + 'PHPUnit' => NULL, + 'functions' => array(), + 'extensions' => array() + ); /** * The name of the test case. * - * @var string + * @var string */ private $name = NULL; /** - * @var array + * @var array */ private $dependencies = array(); /** - * @var array + * @var array */ private $dependencyInput = array(); /** - * @var string - */ - private $exceptionMessage = NULL; - - /** - * @var integer - */ - private $exceptionCode = 0; - - /** - * @var Array + * @var array */ private $iniSettings = array(); /** - * @var Array + * @var array */ private $locale = array(); /** - * @var Array + * @var array */ private $mockObjects = array(); /** - * @var integer + * @var integer */ private $status; /** - * @var string + * @var string */ private $statusMessage = ''; /** - * @var integer + * @var integer */ private $numAssertions = 0; @@ -265,6 +257,36 @@ abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert imple */ 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. * @@ -339,6 +361,90 @@ public function getName($withDataSet = TRUE) } } + /** + * 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 @@ -354,12 +460,11 @@ public function getExpectedException() * @param integer $exceptionCode * @since Method available since Release 3.2.0 */ - public function setExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = 0) + public function setExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = NULL) { $this->expectedException = $exceptionName; $this->expectedExceptionMessage = $exceptionMessage; $this->expectedExceptionCode = $exceptionCode; - $this->expectedExceptionTrace = debug_backtrace(); } /** @@ -441,6 +546,91 @@ protected function setUseOutputBufferingFromAnnotation() } } + /** + * @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. * @@ -483,7 +673,7 @@ public function hasFailed() * * @param PHPUnit_Framework_TestResult $result * @return PHPUnit_Framework_TestResult - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function run(PHPUnit_Framework_TestResult $result = NULL) { @@ -491,11 +681,11 @@ public function run(PHPUnit_Framework_TestResult $result = NULL) $result = $this->createResult(); } - $this->result = $result; - - $this->setExpectedExceptionFromAnnotation(); - $this->setUseErrorHandlerFromAnnotation(); - $this->setUseOutputBufferingFromAnnotation(); + if (!$this instanceof PHPUnit_Framework_Warning) { + $this->setTestResultObject($result); + $this->setUseErrorHandlerFromAnnotation(); + $this->setUseOutputBufferingFromAnnotation(); + } if ($this->useErrorHandler !== NULL) { $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions(); @@ -516,7 +706,7 @@ public function run(PHPUnit_Framework_TestResult $result = NULL) sprintf( '%s%sProcess%sTestCaseMethod.tpl', - dirname(__FILE__), + __DIR__, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR @@ -545,11 +735,14 @@ public function run(PHPUnit_Framework_TestResult $result = NULL) $strict = 'FALSE'; } - $data = addcslashes(serialize($this->data), "'"); - $dependencyInput = addcslashes( - serialize($this->dependencyInput), "'" - ); - $includePath = addslashes(get_include_path()); + $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( @@ -602,8 +795,7 @@ public function runBare() ); } - if (version_compare(PHP_VERSION, '5.3', '>') && - $this->backupStaticAttributes === TRUE) { + if ($this->backupStaticAttributes === TRUE) { PHPUnit_Util_GlobalState::backupStaticAttributes( $this->backupStaticAttributesBlacklist ); @@ -611,19 +803,23 @@ public function runBare() } // Start output buffering. - if ($this->useOutputBuffering === TRUE) { - ob_start(); - } + 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(); @@ -651,6 +847,9 @@ public function runBare() $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 { @@ -668,13 +867,25 @@ public function runBare() } // Stop output buffering. - if ($this->useOutputBuffering === TRUE) { - ob_end_clean(); + 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) { @@ -685,8 +896,7 @@ public function runBare() ); } - if (version_compare(PHP_VERSION, '5.3', '>') && - $this->backupStaticAttributes === TRUE) { + if ($this->backupStaticAttributes === TRUE) { PHPUnit_Util_GlobalState::restoreStaticAttributes(); } } @@ -703,6 +913,27 @@ public function runBare() 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); @@ -713,7 +944,7 @@ public function runBare() * Override to run the test and assert its state. * * @return mixed - * @throws RuntimeException + * @throws PHPUnit_Framework_Exception */ protected function runTest() { @@ -739,27 +970,50 @@ protected function runTest() } catch (Exception $e) { - if (!$e instanceof PHPUnit_Framework_IncompleteTest && - !$e instanceof PHPUnit_Framework_SkippedTest && - is_string($this->expectedException) && - $e instanceof $this->expectedException) { + $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->assertContains( - $this->expectedExceptionMessage, - $e->getMessage() + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionMessage( + $this->expectedExceptionMessage + ) ); } - if (is_int($this->expectedExceptionCode) && - $this->expectedExceptionCode !== 0) { - $this->assertEquals( - $this->expectedExceptionCode, $e->getCode() + if ($this->expectedExceptionCode !== NULL) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionCode( + $this->expectedExceptionCode + ) ); } - $this->numAssertions++; - return; } else { throw $e; @@ -767,13 +1021,11 @@ protected function runTest() } if ($this->expectedException !== NULL) { - $this->numAssertions++; - - $this->syntheticFail( - 'Expected exception ' . $this->expectedException, - '', - 0, - $this->expectedExceptionTrace + $this->assertThat( + NULL, + new PHPUnit_Framework_Constraint_Exception( + $this->expectedException + ) ); } @@ -788,12 +1040,13 @@ protected function runTest() protected function verifyMockObjects() { foreach ($this->mockObjects as $mockObject) { - $this->numAssertions++; + if ($mockObject->__phpunit_hasMatchers()) { + $this->numAssertions++; + } + $mockObject->__phpunit_verify(); $mockObject->__phpunit_cleanup(); } - - $this->mockObjects = array(); } /** @@ -857,7 +1110,7 @@ public function setBackupStaticAttributes($backupStaticAttributes) /** * @param boolean $runTestInSeparateProcess - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.4.0 */ public function setRunTestInSeparateProcess($runTestInSeparateProcess) @@ -873,7 +1126,7 @@ public function setRunTestInSeparateProcess($runTestInSeparateProcess) /** * @param boolean $preserveGlobalState - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.4.0 */ public function setPreserveGlobalState($preserveGlobalState) @@ -887,7 +1140,7 @@ public function setPreserveGlobalState($preserveGlobalState) /** * @param boolean $inIsolation - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.4.0 */ public function setInIsolation($inIsolation) @@ -917,6 +1170,20 @@ 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 @@ -926,6 +1193,15 @@ 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 @@ -933,8 +1209,7 @@ public function getTestResultObject() * * @param string $varName * @param string $newValue - * @throws InvalidArgumentException - * @throws RuntimeException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.0.0 */ protected function iniSet($varName, $newValue) @@ -964,8 +1239,7 @@ protected function iniSet($varName, $newValue) * * @param integer $category * @param string $locale - * @throws InvalidArgumentException - * @throws RuntimeException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.1.0 */ protected function setLocale() @@ -973,7 +1247,7 @@ protected function setLocale() $args = func_get_args(); if (count($args) < 2) { - throw new InvalidArgumentException; + throw new PHPUnit_Framework_Exception; } $category = $args[0]; @@ -988,11 +1262,11 @@ protected function setLocale() } if (!in_array($category, $categories)) { - throw new InvalidArgumentException; + throw new PHPUnit_Framework_Exception; } if (!is_array($locale) && !is_string($locale)) { - throw new InvalidArgumentException; + throw new PHPUnit_Framework_Exception; } $this->locale[$category] = setlocale($category, NULL); @@ -1018,11 +1292,12 @@ protected function setLocale() * @param boolean $callOriginalConstructor * @param boolean $callOriginalClone * @param boolean $callAutoload + * @param boolean $cloneArguments * @return PHPUnit_Framework_MockObject_MockObject - * @throws InvalidArgumentException + * @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) + 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, @@ -1031,7 +1306,8 @@ public function getMock($originalClassName, $methods = array(), array $arguments $mockClassName, $callOriginalConstructor, $callOriginalClone, - $callAutoload + $callAutoload, + $cloneArguments ); $this->mockObjects[] = $mockObject; @@ -1063,11 +1339,12 @@ public function getMockBuilder($className) * @param boolean $callOriginalConstructor * @param boolean $callOriginalClone * @param boolean $callAutoload + * @param boolean $cloneArguments * @return string - * @throws InvalidArgumentException + * @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) + protected function getMockClass($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = FALSE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = FALSE) { $mock = $this->getMock( $originalClassName, @@ -1076,7 +1353,8 @@ protected function getMockClass($originalClassName, $methods = array(), array $a $mockClassName, $callOriginalConstructor, $callOriginalClone, - $callAutoload + $callAutoload, + $cloneArguments ); return get_class($mock); @@ -1084,7 +1362,8 @@ protected function getMockClass($originalClassName, $methods = array(), array $a /** * Returns a mock object for the specified abstract class with all abstract - * methods of the class mocked. Concrete methods are not mocked. + * methods of the class mocked. Concrete methods to mock can be specified with + * the last parameter * * @param string $originalClassName * @param array $arguments @@ -1092,11 +1371,13 @@ protected function getMockClass($originalClassName, $methods = array(), array $a * @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 InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ - public function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) + 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, @@ -1104,7 +1385,9 @@ public function getMockForAbstractClass($originalClassName, array $arguments = a $mockClassName, $callOriginalConstructor, $callOriginalClone, - $callAutoload + $callAutoload, + $mockedMethods, + $cloneArguments ); $this->mockObjects[] = $mockObject; @@ -1131,11 +1414,13 @@ protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClas ); } - eval( - PHPUnit_Framework_MockObject_Generator::generateClassFromWsdl( - $wsdlFile, $originalClassName, $methods - ) - ); + if (!class_exists($originalClassName)) { + eval( + PHPUnit_Framework_MockObject_Generator::generateClassFromWsdl( + $wsdlFile, $originalClassName, $methods + ) + ); + } return $this->getMock( $originalClassName, @@ -1148,6 +1433,33 @@ protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClas ); } + /** + * 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. * @@ -1256,6 +1568,18 @@ 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); + } + /** * * @@ -1282,6 +1606,19 @@ 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(); + } + /** * * @@ -1317,8 +1654,26 @@ protected function dataToString($data) { $result = array(); - foreach ($data as $_data) { - if (is_array($_data)) { + // 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) . ')'; } @@ -1418,12 +1773,24 @@ protected function handleDependencies() ); return FALSE; - } else { - if (isset($passed[$dependency])) { - $this->dependencyInput[] = $passed[$dependency]; - } else { - $this->dependencyInput[] = NULL; + } + + 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; } } } diff --git a/libs/PHPUnit/PHPUnit/Framework/TestFailure.php b/libs/PHPUnit/PHPUnit/Framework/TestFailure.php index b78803f..5bde6af 100644 --- a/libs/PHPUnit/PHPUnit/Framework/TestFailure.php +++ b/libs/PHPUnit/PHPUnit/Framework/TestFailure.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -115,62 +114,14 @@ public function getExceptionAsString() public static function exceptionToString(Exception $e) { if ($e instanceof PHPUnit_Framework_SelfDescribing) { - if ($e instanceof PHPUnit_Framework_ExpectationFailedException) { - $comparisonFailure = $e->getComparisonFailure(); - $description = $e->getDescription(); - $message = $e->getCustomMessage(); - - if ($message == '') { - $buffer = ''; - } else { - $buffer = $message . "\n"; - } - - if ($comparisonFailure !== NULL) { - if ($comparisonFailure->identical()) { - if ($comparisonFailure instanceof PHPUnit_Framework_ComparisonFailure_Object) { - $buffer .= 'Failed asserting that two variables ' . - "reference the same object.\n"; - } else { - $buffer .= $comparisonFailure->toString() . "\n"; - } - } else { - if ($comparisonFailure instanceof PHPUnit_Framework_ComparisonFailure_Scalar) { - $buffer .= $comparisonFailure->toString() . "\n"; - } - - else if ($comparisonFailure instanceof PHPUnit_Framework_ComparisonFailure_Array || - $comparisonFailure instanceof PHPUnit_Framework_ComparisonFailure_Object || - $comparisonFailure instanceof PHPUnit_Framework_ComparisonFailure_String) { - $buffer .= sprintf( - "Failed asserting that two %ss are equal.\n%s\n", - - strtolower( - substr(get_class($comparisonFailure), 36) - ), - $comparisonFailure->toString() - ); - } - } - } else { - $buffer .= $e->toString(); - - if (!empty($buffer)) { - $buffer .= "\n"; - } - - if (strpos($buffer, $description) === FALSE) { - $buffer .= $description . "\n"; - } - } - } + $buffer = $e->toString(); - else { - $buffer = $e->toString(); + if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) { + $buffer = $buffer . "\n" . $e->getComparisonFailure()->getDiff(); + } - if (!empty($buffer)) { - $buffer .= "\n"; - } + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; } } diff --git a/libs/PHPUnit/PHPUnit/Framework/TestListener.php b/libs/PHPUnit/PHPUnit/Framework/TestListener.php index c9e51e5..e25fef7 100644 --- a/libs/PHPUnit/PHPUnit/Framework/TestListener.php +++ b/libs/PHPUnit/PHPUnit/Framework/TestListener.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/TestResult.php b/libs/PHPUnit/PHPUnit/Framework/TestResult.php index 1ededf8..2df29ab 100644 --- a/libs/PHPUnit/PHPUnit/Framework/TestResult.php +++ b/libs/PHPUnit/PHPUnit/Framework/TestResult.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,24 +37,20 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'PHP/CodeCoverage.php'; -require_once 'PHP/Timer.php'; - /** * A TestResult collects the results of executing a test case. * * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -123,25 +119,10 @@ class PHPUnit_Framework_TestResult implements Countable /** * Code Coverage information. * - * @var array + * @var PHP_CodeCoverage */ protected $codeCoverage; - /** - * @var boolean - */ - protected $collectCodeCoverageInformation = FALSE; - - /** - * @var boolean - */ - protected $collectRawCodeCoverageInformation = FALSE; - - /** - * @var array - */ - protected $rawCodeCoverageInformation = array(); - /** * @var boolean */ @@ -183,16 +164,19 @@ class PHPUnit_Framework_TestResult implements Countable protected $lastTestFailed = FALSE; /** - * @param PHP_CodeCoverage $codeCoverage + * @var integer + */ + protected $timeoutForSmallTests = 1; + + /** + * @var integer */ - public function __construct(PHP_CodeCoverage $codeCoverage = NULL) - { - if ($codeCoverage === NULL) { - $codeCoverage = PHP_CodeCoverage::getInstance(); - } + protected $timeoutForMediumTests = 10; - $this->codeCoverage = $codeCoverage; - } + /** + * @var integer + */ + protected $timeoutForLargeTests = 60; /** * Registers a TestListener. @@ -395,8 +379,17 @@ public function endTest(PHPUnit_Framework_Test $test, $time) } if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) { - $this->passed[get_class($test) . '::' . $test->getName()] = $test->getResult(); - $this->time += $time; + $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; } } @@ -547,42 +540,6 @@ public function topTestSuite() return $this->topTestSuite; } - /** - * Enables or disables the collection of Code Coverage information. - * - * @param boolean $flag - * @throws InvalidArgumentException - * @since Method available since Release 2.3.0 - */ - public function collectCodeCoverageInformation($flag) - { - if (is_bool($flag)) { - $this->collectCodeCoverageInformation = $flag; - } else { - throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); - } - } - - /** - * Enables or disables the collection of raw Code Coverage information. - * - * @param boolean $flag - * @throws InvalidArgumentException - * @since Method available since Release 3.4.0 - */ - public function collectRawCodeCoverageInformation($flag) - { - if (is_bool($flag)) { - $this->collectRawCodeCoverageInformation = $flag; - - if ($flag === TRUE) { - $this->collectCodeCoverageInformation = $flag; - } - } else { - throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); - } - } - /** * Returns whether code coverage information should be collected. * @@ -591,18 +548,7 @@ public function collectRawCodeCoverageInformation($flag) */ public function getCollectCodeCoverageInformation() { - return $this->collectCodeCoverageInformation; - } - - /** - * Returns the raw Code Coverage information. - * - * @return array - * @since Method available since Release 3.4.0 - */ - public function getRawCodeCoverageInformation() - { - return $this->rawCodeCoverageInformation; + return $this->codeCoverage !== NULL; } /** @@ -652,18 +598,55 @@ public function run(PHPUnit_Framework_Test $test) } $useXdebug = self::$useXdebug && - $this->collectCodeCoverageInformation && + $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 { - $test->runBare(); + 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) { @@ -690,23 +673,17 @@ public function run(PHPUnit_Framework_Test $test) } if ($useXdebug) { - $data = $this->codeCoverage->stop(FALSE); - - if (!$incomplete && !$skipped) { - if ($this->collectRawCodeCoverageInformation) { - $this->rawCodeCoverageInformation[] = $data; - } else { - $filterGroups = array('DEFAULT', 'TESTS'); + try { + $this->codeCoverage->stop(!$incomplete && !$skipped); + } - if (!defined('PHPUNIT_TESTSUITE')) { - $filterGroups[] = 'PHPUNIT'; - } + catch (PHP_CodeCoverage_Exception $cce) { + $error = TRUE; - $this->codeCoverage->append($data, $test, $filterGroups); + if (!isset($e)) { + $e = $cce; } } - - unset($data); } if ($errorHandlerSet === TRUE) { @@ -731,6 +708,19 @@ public function run(PHPUnit_Framework_Test $test) ); } + 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); } @@ -774,11 +764,22 @@ 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 InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.2.14 */ public function convertErrorsToExceptions($flag) @@ -805,7 +806,7 @@ public function getConvertErrorsToExceptions() * Enables or disables the stopping when an error occurs. * * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.5.0 */ public function stopOnError($flag) @@ -821,7 +822,7 @@ public function stopOnError($flag) * Enables or disables the stopping when a failure occurs. * * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.1.0 */ public function stopOnFailure($flag) @@ -841,7 +842,7 @@ public function stopOnFailure($flag) * * Tests that are incomplete or skipped yield no code coverage. * * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.5.2 */ public function strictMode($flag) @@ -857,7 +858,7 @@ public function strictMode($flag) * Enables or disables the stopping for incomplete tests. * * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.5.0 */ public function stopOnIncomplete($flag) @@ -873,7 +874,7 @@ public function stopOnIncomplete($flag) * Enables or disables the stopping for skipped tests. * * @param boolean $flag - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.1.0 */ public function stopOnSkipped($flag) @@ -904,4 +905,52 @@ 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/libs/PHPUnit/PHPUnit/Framework/TestSuite.php b/libs/PHPUnit/PHPUnit/Framework/TestSuite.php index 45acd93..b78c39b 100644 --- a/libs/PHPUnit/PHPUnit/Framework/TestSuite.php +++ b/libs/PHPUnit/PHPUnit/Framework/TestSuite.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,14 +37,12 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'PHP/CodeCoverage.php'; - /** * A TestSuite is a composite of Tests. It runs a collection of test cases. * @@ -76,9 +74,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -150,7 +147,7 @@ class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Fra * * @param mixed $theClass * @param string $name - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function __construct($theClass = '', $name = '') { @@ -179,20 +176,12 @@ class_exists($theClass, FALSE)) { } if (!$argumentsValid) { - throw new InvalidArgumentException; + throw new PHPUnit_Framework_Exception; } if (!$theClass->isSubclassOf('PHPUnit_Framework_TestCase')) { - throw new InvalidArgumentException( - 'Class does not extend PHPUnit_Framework_TestCase.' - ); - } - - $filename = $theClass->getFilename(); - - if (strpos($filename, 'eval()') === FALSE) { - PHP_CodeCoverage::getInstance()->filter()->addFileToBlacklist( - realpath($filename), 'TESTS' + throw new PHPUnit_Framework_Exception( + 'Class "' . $theClass->name . '" does not extend PHPUnit_Framework_TestCase.' ); } @@ -220,9 +209,7 @@ class_exists($theClass, FALSE)) { } foreach ($theClass->getMethods() as $method) { - if (strpos($method->getDeclaringClass()->getName(), 'PHPUnit_') !== 0) { - $this->addTestMethod($theClass, $method); - } + $this->addTestMethod($theClass, $method); } if (empty($this->tests)) { @@ -287,7 +274,7 @@ public function addTest(PHPUnit_Framework_Test $test, $groups = array()) * Adds the tests from the given class to the suite. * * @param mixed $testClass - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function addTestSuite($testClass) { @@ -330,7 +317,7 @@ public function addTestSuite($testClass) } else { - throw new InvalidArgumentException; + throw new PHPUnit_Framework_Exception; } } @@ -343,15 +330,14 @@ public function addTestSuite($testClass) * leaving the current test run untouched. * * @param string $filename - * @param boolean $syntaxCheck * @param array $phptOptions Array with ini settings for the php instance * run, key being the name if the setting, * value the ini value. - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 2.3.0 * @author Stefano F. Rausch */ - public function addTestFile($filename, $syntaxCheck = FALSE, $phptOptions = array()) + public function addTestFile($filename, $phptOptions = array()) { if (!is_string($filename)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); @@ -366,7 +352,7 @@ public function addTestFile($filename, $syntaxCheck = FALSE, $phptOptions = arra } PHPUnit_Util_Class::collectStart(); - $filename = PHPUnit_Util_Fileloader::checkAndLoad($filename, $syntaxCheck); + $filename = PHPUnit_Util_Fileloader::checkAndLoad($filename); $newClasses = PHPUnit_Util_Class::collectEnd(); $baseName = str_replace('.php', '', basename($filename)); @@ -414,10 +400,10 @@ public function addTestFile($filename, $syntaxCheck = FALSE, $phptOptions = arra * Wrapper for addTestFile() that adds multiple test files. * * @param array|Iterator $filenames - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 2.3.0 */ - public function addTestFiles($filenames, $syntaxCheck = FALSE) + public function addTestFiles($filenames) { if (!(is_array($filenames) || (is_object($filenames) && $filenames instanceof Iterator))) { @@ -427,7 +413,7 @@ public function addTestFiles($filenames, $syntaxCheck = FALSE) } foreach ($filenames as $filename) { - $this->addTestFile((string)$filename, $syntaxCheck); + $this->addTestFile((string)$filename); } } @@ -455,7 +441,7 @@ public function count() * @param ReflectionClass $theClass * @param string $name * @return PHPUnit_Framework_Test - * @throws RuntimeException + * @throws PHPUnit_Framework_Exception */ public static function createTest(ReflectionClass $theClass, $name) { @@ -568,7 +554,7 @@ public static function createTest(ReflectionClass $theClass, $name) } if (!isset($test)) { - throw new RuntimeException('No valid test provided.'); + throw new PHPUnit_Framework_Exception('No valid test provided.'); } if ($test instanceof PHPUnit_Framework_TestCase) { @@ -636,7 +622,7 @@ public function getGroups() * @param array $excludeGroups * @param boolean $processIsolation * @return PHPUnit_Framework_TestResult - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function run(PHPUnit_Framework_TestResult $result = NULL, $filter = FALSE, array $groups = array(), array $excludeGroups = array(), $processIsolation = FALSE) { @@ -646,23 +632,49 @@ public function run(PHPUnit_Framework_TestResult $result = NULL, $filter = FALSE $result->startTestSuite($this); - try { - $this->setUp(); + $doSetup = TRUE; - if ($this->testCase && - method_exists($this->name, 'setUpBeforeClass')) { - call_user_func(array($this->name, 'setUpBeforeClass')); + if (!empty($excludeGroups)) { + foreach ($this->groups as $_group => $_tests) { + if (in_array($_group, $excludeGroups) && + count($_tests) == count($this->tests)) { + $doSetup = FALSE; + } } } - catch (PHPUnit_Framework_SkippedTestSuiteError $e) { - $numTests = count($this); + if ($doSetup) { + try { + $this->setUp(); - for ($i = 0; $i < $numTests; $i++) { - $result->addFailure($this, $e, 0); + 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')); + } } - return $result; + 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)) { @@ -735,12 +747,18 @@ public function run(PHPUnit_Framework_TestResult $result = NULL, $filter = FALSE } } - if ($this->testCase && - method_exists($this->name, 'tearDownAfterClass')) { - call_user_func(array($this->name, 'tearDownAfterClass')); + 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(); } - $this->tearDown(); $result->endTestSuite($this); return $result; @@ -750,7 +768,7 @@ public function run(PHPUnit_Framework_TestResult $result = NULL, $filter = FALSE * Runs a test. * * @param PHPUnit_Framework_Test $test - * @param PHPUnit_Framework_TestResult $testResult + * @param PHPUnit_Framework_TestResult $result */ public function runTest(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result) { diff --git a/libs/PHPUnit/PHPUnit/Framework/TestSuite/DataProvider.php b/libs/PHPUnit/PHPUnit/Framework/TestSuite/DataProvider.php index e2becf9..31f0808 100644 --- a/libs/PHPUnit/PHPUnit/Framework/TestSuite/DataProvider.php +++ b/libs/PHPUnit/PHPUnit/Framework/TestSuite/DataProvider.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework_TestSuite * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework_TestSuite * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Framework/Warning.php b/libs/PHPUnit/PHPUnit/Framework/Warning.php index d201c6d..72b30d8 100644 --- a/libs/PHPUnit/PHPUnit/Framework/Warning.php +++ b/libs/PHPUnit/PHPUnit/Framework/Warning.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Framework * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -97,7 +96,7 @@ public function __construct($message = '') } /** - * @throws RuntimeException + * @throws PHPUnit_Framework_Exception */ protected function runTest() { diff --git a/libs/PHPUnit/PHPUnit/Runner/BaseTestRunner.php b/libs/PHPUnit/PHPUnit/Runner/BaseTestRunner.php index d4f99e3..c80af8f 100644 --- a/libs/PHPUnit/PHPUnit/Runner/BaseTestRunner.php +++ b/libs/PHPUnit/PHPUnit/Runner/BaseTestRunner.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -81,26 +80,27 @@ public function getLoader() * * @param string $suiteClassName * @param string $suiteClassFile - * @param boolean $syntaxCheck + * @param mixed $suffixes * @return PHPUnit_Framework_Test */ - public function getTest($suiteClassName, $suiteClassFile = '', $syntaxCheck = FALSE) + public function getTest($suiteClassName, $suiteClassFile = '', $suffixes = '') { if (is_dir($suiteClassName) && !is_file($suiteClassName . '.php') && empty($suiteClassFile)) { - $testCollector = new PHPUnit_Runner_IncludePathTestCollector( - array($suiteClassName) + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $suiteClassName, $suffixes ); $suite = new PHPUnit_Framework_TestSuite($suiteClassName); - $suite->addTestFiles($testCollector->collectTests(), $syntaxCheck); + $suite->addTestFiles($files); return $suite; } try { $testClass = $this->loadSuiteClass( - $suiteClassName, $suiteClassFile, $syntaxCheck + $suiteClassName, $suiteClassFile ); } @@ -142,7 +142,7 @@ public function getTest($suiteClassName, $suiteClassFile = '', $syntaxCheck = FA $test = new PHPUnit_Framework_TestSuite($testClass); } - catch (InvalidArgumentException $e) { + catch (PHPUnit_Framework_Exception $e) { $test = new PHPUnit_Framework_TestSuite; $test->setName($suiteClassName); } @@ -158,15 +158,14 @@ public function getTest($suiteClassName, $suiteClassFile = '', $syntaxCheck = FA * * @param string $suiteClassName * @param string $suiteClassFile - * @param boolean $syntaxCheck * @return ReflectionClass */ - protected function loadSuiteClass($suiteClassName, $suiteClassFile = '', $syntaxCheck = FALSE) + protected function loadSuiteClass($suiteClassName, $suiteClassFile = '') { $loader = $this->getLoader(); if ($loader instanceof PHPUnit_Runner_StandardTestSuiteLoader) { - return $loader->load($suiteClassName, $suiteClassFile, $syntaxCheck); + return $loader->load($suiteClassName, $suiteClassFile); } else { return $loader->load($suiteClassName, $suiteClassFile); } diff --git a/libs/PHPUnit/PHPUnit/Runner/IncludePathTestCollector.php b/libs/PHPUnit/PHPUnit/Runner/IncludePathTestCollector.php deleted file mode 100644 index bf6f131..0000000 --- a/libs/PHPUnit/PHPUnit/Runner/IncludePathTestCollector.php +++ /dev/null @@ -1,144 +0,0 @@ -. - * 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 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 2.1.0 - */ - -require_once 'File/Iterator/Factory.php'; - -/** - * A test collector that collects tests from one or more directories - * recursively. If no directories are specified, the include_path is searched. - * - * - * $testCollector = new PHPUnit_Runner_IncludePathTestCollector( - * array('/path/to/*Test.php files') - * ); - * - * $suite = new PHPUnit_Framework_TestSuite('My Test Suite'); - * $suite->addTestFiles($testCollector->collectTests()); - * - * - * @package PHPUnit - * @subpackage Runner - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 2.1.0 - */ -class PHPUnit_Runner_IncludePathTestCollector implements PHPUnit_Runner_TestCollector -{ - /** - * @var string - */ - protected $filterIterator; - - /** - * @var array - */ - protected $paths; - - /** - * @var mixed - */ - protected $suffixes; - - /** - * @var mixed - */ - protected $prefixes; - - /** - * @param array $paths - * @param mixed $suffixes - * @param mixed $prefixes - */ - public function __construct(array $paths = array(), $suffixes = array('Test.php', '.phpt'), $prefixes = array()) - { - if (!empty($paths)) { - $this->paths = $paths; - } else { - $this->paths = explode(PATH_SEPARATOR, get_include_path()); - } - - $this->suffixes = $suffixes; - $this->prefixes = $prefixes; - } - - /** - * @return File_Iterator - */ - public function collectTests() - { - $iterator = File_Iterator_Factory::getFileIterator( - $this->paths, $this->suffixes, $this->prefixes - ); - - if ($this->filterIterator !== NULL) { - $class = new ReflectionClass($this->filterIterator); - $iterator = $class->newInstance($iterator); - } - - return $iterator; - } - - /** - * Adds a FilterIterator to filter the source files to be collected. - * - * @param string $filterIterator - * @throws InvalidArgumentException - */ - public function setFilterIterator($filterIterator) - { - if (is_string($filterIterator) && class_exists($filterIterator)) { - $class = new ReflectionClass($filterIterator); - - if ($class->isSubclassOf('FilterIterator')) { - $this->filterIterator = $filterIterator; - } - } else { - throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class name'); - } - } -} diff --git a/libs/PHPUnit/PHPUnit/Runner/StandardTestSuiteLoader.php b/libs/PHPUnit/PHPUnit/Runner/StandardTestSuiteLoader.php index 3fd14c6..88574e2 100644 --- a/libs/PHPUnit/PHPUnit/Runner/StandardTestSuiteLoader.php +++ b/libs/PHPUnit/PHPUnit/Runner/StandardTestSuiteLoader.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -60,11 +59,10 @@ class PHPUnit_Runner_StandardTestSuiteLoader implements PHPUnit_Runner_TestSuite /** * @param string $suiteClassName * @param string $suiteClassFile - * @param boolean $syntaxCheck * @return ReflectionClass - * @throws RuntimeException + * @throws PHPUnit_Framework_Exception */ - public function load($suiteClassName, $suiteClassFile = '', $syntaxCheck = FALSE) + public function load($suiteClassName, $suiteClassFile = '') { $suiteClassName = str_replace('.php', '', $suiteClassName); @@ -76,7 +74,7 @@ public function load($suiteClassName, $suiteClassFile = '', $syntaxCheck = FALSE if (!class_exists($suiteClassName, FALSE)) { PHPUnit_Util_Class::collectStart(); - PHPUnit_Util_Fileloader::checkAndLoad($suiteClassFile, $syntaxCheck); + $filename = PHPUnit_Util_Fileloader::checkAndLoad($suiteClassFile); $loadedClasses = PHPUnit_Util_Class::collectEnd(); } @@ -84,7 +82,9 @@ public function load($suiteClassName, $suiteClassFile = '', $syntaxCheck = FALSE $offset = 0 - strlen($suiteClassName); foreach ($loadedClasses as $loadedClass) { - if (substr($loadedClass, $offset) === $suiteClassName) { + $class = new ReflectionClass($loadedClass); + if (substr($loadedClass, $offset) === $suiteClassName && + $class->getFileName() == $filename) { $suiteClassName = $loadedClass; break; } @@ -134,7 +134,7 @@ public function load($suiteClassName, $suiteClassFile = '', $syntaxCheck = FALSE throw new PHPUnit_Framework_Exception( sprintf( - 'Class %s could not be found in %s.', + "Class '%s' could not be found in '%s'.", $suiteClassName, $suiteClassFile diff --git a/libs/PHPUnit/PHPUnit/Runner/TestSuiteLoader.php b/libs/PHPUnit/PHPUnit/Runner/TestSuiteLoader.php index 2129e1d..7cad44f 100644 --- a/libs/PHPUnit/PHPUnit/Runner/TestSuiteLoader.php +++ b/libs/PHPUnit/PHPUnit/Runner/TestSuiteLoader.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Runner/Version.php b/libs/PHPUnit/PHPUnit/Runner/Version.php index 849ddef..89f7e52 100644 --- a/libs/PHPUnit/PHPUnit/Runner/Version.php +++ b/libs/PHPUnit/PHPUnit/Runner/Version.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,14 +49,16 @@ * @package PHPUnit * @subpackage Runner * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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.7'; + protected static $version; + /** * Returns the current version of PHPUnit. * @@ -64,7 +66,28 @@ class PHPUnit_Runner_Version */ public static function id() { - return '3.5.14'; + 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; } /** @@ -72,6 +95,6 @@ public static function id() */ public static function getVersionString() { - return 'PHPUnit 3.5.14 by Sebastian Bergmann.'; + return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann.'; } } diff --git a/libs/PHPUnit/PHPUnit/TextUI/Command.php b/libs/PHPUnit/PHPUnit/TextUI/Command.php index 36d75a2..cc5bac7 100644 --- a/libs/PHPUnit/PHPUnit/TextUI/Command.php +++ b/libs/PHPUnit/PHPUnit/TextUI/Command.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage TextUI * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @package PHPUnit * @subpackage TextUI * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -64,7 +63,6 @@ class PHPUnit_TextUI_Command protected $arguments = array( 'listGroups' => FALSE, 'loader' => NULL, - 'syntaxCheck' => FALSE, 'useDefaultConfiguration' => TRUE ); @@ -82,51 +80,53 @@ class PHPUnit_TextUI_Command '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-dbus' => NULL, 'log-json=' => NULL, 'log-junit=' => NULL, 'log-tap=' => NULL, 'process-isolation' => NULL, 'repeat=' => NULL, - 'skeleton-class' => NULL, - 'skeleton-test' => NULL, 'stderr' => NULL, 'stop-on-error' => NULL, 'stop-on-failure' => NULL, 'stop-on-incomplete' => NULL, 'stop-on-skipped' => NULL, - 'story' => NULL, - 'story-html=' => NULL, - 'story-text=' => NULL, 'strict' => NULL, - 'syntax-check' => 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, - 'wait' => NULL + 'version' => NULL ); + /** + * @var array + */ + protected $missingExtensions = array(); + /** * @param boolean $exit */ public static function main($exit = TRUE) { $command = new PHPUnit_TextUI_Command; - $command->run($_SERVER['argv'], $exit); + return $command->run($_SERVER['argv'], $exit); } /** @@ -137,7 +137,7 @@ public function run(array $argv, $exit = TRUE) { $this->handleArguments($argv); - $runner = new PHPUnit_TextUI_TestRunner($this->arguments['loader']); + $runner = $this->createRunner(); if (is_object($this->arguments['test']) && $this->arguments['test'] instanceof PHPUnit_Framework_Test) { @@ -146,26 +146,10 @@ public function run(array $argv, $exit = TRUE) $suite = $runner->getTest( $this->arguments['test'], $this->arguments['testFile'], - $this->arguments['syntaxCheck'] + $this->arguments['testSuffixes'] ); } - if (count($suite) == 0) { - $skeleton = new PHPUnit_Util_Skeleton_Test( - $suite->getName(), - $this->arguments['testFile'] - ); - - $result = $skeleton->generate(TRUE); - - if (!$result['incomplete']) { - eval(str_replace(array(''), '', $result['code'])); - $suite = new PHPUnit_Framework_TestSuite( - $this->arguments['test'] . 'Test' - ); - } - } - if ($this->arguments['listGroups']) { PHPUnit_TextUI_TestRunner::printVersionString(); @@ -178,7 +162,11 @@ public function run(array $argv, $exit = TRUE) print " - $group\n"; } - exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + if ($exit) { + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } else { + return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; + } } unset($this->arguments['test']); @@ -192,21 +180,34 @@ public function run(array $argv, $exit = TRUE) print $e->getMessage() . "\n"; } - if ($exit) { - if (isset($result) && $result->wasSuccessful()) { - exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); - } + $ret = PHPUnit_TextUI_TestRunner::FAILURE_EXIT; - else if (!isset($result) || $result->errorCount() > 0) { - exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); - } + if (isset($result) && $result->wasSuccessful()) { + $ret = PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; + } - else { - exit(PHPUnit_TextUI_TestRunner::FAILURE_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. * @@ -237,18 +238,15 @@ protected function handleArguments(array $argv) try { $this->options = PHPUnit_Util_Getopt::getopt( $argv, - 'd:c:', + 'd:c:hv', array_keys($this->longOptions) ); } - catch (RuntimeException $e) { + catch (PHPUnit_Framework_Exception $e) { PHPUnit_TextUI_TestRunner::showError($e->getMessage()); } - $skeletonClass = FALSE; - $skeletonTest = FALSE; - foreach ($this->options[0] as $option) { switch ($option[0]) { case '--colors': { @@ -267,38 +265,51 @@ protected function handleArguments(array $argv) } break; - case '--coverage-clover': { - if (extension_loaded('tokenizer') && - extension_loaded('xdebug')) { - $this->arguments['coverageClover'] = $option[1]; - } else { - if (!extension_loaded('tokenizer')) { - $this->showMessage( - 'The tokenizer extension is not loaded.' - ); - } else { - $this->showMessage( - 'The Xdebug extension is not loaded.' - ); - } + 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; } - } - break; - case '--coverage-html': { - if (extension_loaded('tokenizer') && - extension_loaded('xdebug')) { - $this->arguments['reportDirectory'] = $option[1]; - } else { - if (!extension_loaded('tokenizer')) { - $this->showMessage( - 'The tokenizer extension is not loaded.' - ); - } else { - $this->showMessage( - 'The Xdebug extension is not loaded.' - ); + 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; @@ -321,6 +332,7 @@ protected function handleArguments(array $argv) } break; + case 'h': case '--help': { $this->showHelp(); exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); @@ -332,6 +344,11 @@ protected function handleArguments(array $argv) } break; + case '--testsuite': { + $this->arguments['testsuite'] = $option[1]; + } + break; + case '--group': { $this->arguments['groups'] = explode(',', $option[1]); } @@ -344,6 +361,13 @@ protected function handleArguments(array $argv) } break; + case '--test-suffix': { + $this->arguments['testSuffixes'] = explode( + ',', $option[1] + ); + } + break; + case '--include-path': { $includePath = $option[1]; } @@ -354,13 +378,13 @@ protected function handleArguments(array $argv) } break; - case '--loader': { - $this->arguments['loader'] = $option[1]; + case '--printer': { + $this->arguments['printer'] = $option[1]; } break; - case '--log-dbus': { - $this->arguments['logDbus'] = TRUE; + case '--loader': { + $this->arguments['loader'] = $option[1]; } break; @@ -381,7 +405,6 @@ protected function handleArguments(array $argv) case '--process-isolation': { $this->arguments['processIsolation'] = TRUE; - $this->arguments['syntaxCheck'] = FALSE; } break; @@ -418,61 +441,11 @@ protected function handleArguments(array $argv) } break; - case '--skeleton-test': { - $skeletonTest = TRUE; - $skeletonClass = FALSE; - } - break; - - case '--skeleton-class': { - $skeletonClass = TRUE; - $skeletonTest = FALSE; - } - break; - case '--tap': { $this->arguments['printer'] = new PHPUnit_Util_Log_TAP; } break; - case '--story': { - $this->showMessage( - 'The --story functionality is deprecated and ' . - 'will be removed in the future.', - FALSE - ); - - $this->arguments['printer'] = new PHPUnit_Extensions_Story_ResultPrinter_Text; - } - break; - - case '--story-html': { - $this->showMessage( - 'The --story-html functionality is deprecated and ' . - 'will be removed in the future.', - FALSE - ); - - $this->arguments['storyHTMLFile'] = $option[1]; - } - break; - - case '--story-text': { - $this->showMessage( - 'The --story-text functionality is deprecated and ' . - 'will be removed in the future.', - FALSE - ); - - $this->arguments['storyTextFile'] = $option[1]; - } - break; - - case '--syntax-check': { - $this->arguments['syntaxCheck'] = TRUE; - } - break; - case '--testdox': { $this->arguments['printer'] = new PHPUnit_Util_TestDox_ResultPrinter_Text; } @@ -503,6 +476,7 @@ protected function handleArguments(array $argv) } break; + case 'v': case '--verbose': { $this->arguments['verbose'] = TRUE; } @@ -514,11 +488,6 @@ protected function handleArguments(array $argv) } break; - case '--wait': { - $this->arguments['wait'] = TRUE; - } - break; - case '--strict': { $this->arguments['strict'] = TRUE; } @@ -542,18 +511,10 @@ protected function handleArguments(array $argv) } } - if (isset($this->arguments['printer']) && - $this->arguments['printer'] instanceof PHPUnit_Extensions_Story_ResultPrinter_Text && - isset($this->arguments['processIsolation']) && - $this->arguments['processIsolation']) { - $this->showMessage( - 'The story result printer cannot be used in process isolation.' - ); - } - $this->handleCustomTestSuite(); if (!isset($this->arguments['test'])) { + if (isset($this->options[1][0])) { $this->arguments['test'] = $this->options[1][0]; } @@ -564,12 +525,18 @@ protected function handleArguments(array $argv) $this->arguments['testFile'] = ''; } - if (isset($this->arguments['test']) && is_file($this->arguments['test'])) { + 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', @@ -578,7 +545,12 @@ protected function handleArguments(array $argv) } if (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap'], $this->arguments['syntaxCheck']); + $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) { @@ -628,8 +600,22 @@ protected function handleArguments(array $argv) $phpunit = $configuration->getPHPUnitConfiguration(); - if (isset($phpunit['syntaxCheck'])) { - $this->arguments['syntaxCheck'] = $phpunit['syntaxCheck']; + $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'])) { @@ -644,26 +630,31 @@ protected function handleArguments(array $argv) ); } - $configuration->handlePHPConfiguration(); + $logging = $configuration->getLoggingConfiguration(); - if (!isset($this->arguments['bootstrap'])) { - $phpunitConfiguration = $configuration->getPHPUnitConfiguration(); + 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.' + ); + } - if (isset($phpunitConfiguration['bootstrap'])) { - $this->handleBootstrap($phpunitConfiguration['bootstrap'], $this->arguments['syntaxCheck']); + else if (!extension_loaded('Xdebug')) { + $this->showExtensionNotLoadedMessage( + 'Xdebug', 'No code coverage will be generated.' + ); } } $browsers = $configuration->getSeleniumBrowserConfiguration(); - if (!empty($browsers)) { + if (!empty($browsers) && + class_exists('PHPUnit_Extensions_SeleniumTestCase')) { PHPUnit_Extensions_SeleniumTestCase::$browsers = $browsers; } if (!isset($this->arguments['test'])) { - $testSuite = $configuration->getTestSuiteConfiguration( - $this->arguments['syntaxCheck'] - ); + $testSuite = $configuration->getTestSuiteConfiguration(isset($this->arguments['testsuite']) ? $this->arguments['testsuite'] : null); if ($testSuite !== NULL) { $this->arguments['test'] = $testSuite; @@ -683,52 +674,6 @@ protected function handleArguments(array $argv) $this->showHelp(); exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); } - - if (!isset($this->arguments['syntaxCheck'])) { - $this->arguments['syntaxCheck'] = FALSE; - } - - if ($skeletonClass || $skeletonTest) { - if (isset($this->arguments['test']) && $this->arguments['test'] !== FALSE) { - PHPUnit_TextUI_TestRunner::printVersionString(); - - if ($skeletonClass) { - $class = 'PHPUnit_Util_Skeleton_Class'; - } else { - $class = 'PHPUnit_Util_Skeleton_Test'; - } - - try { - $args = array(); - $reflector = new ReflectionClass($class); - - for ($i = 0; $i <= 3; $i++) { - if (isset($this->options[1][$i])) { - $args[] = $this->options[1][$i]; - } - } - - $skeleton = $reflector->newInstanceArgs($args); - $skeleton->write(); - } - - catch (Exception $e) { - print $e->getMessage() . "\n"; - exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); - } - - printf( - 'Wrote skeleton for "%s" to "%s".' . "\n", - $skeleton->getOutClassName(), - $skeleton->getOutSourceFile() - ); - - exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); - } else { - $this->showHelp(); - exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); - } - } } /** @@ -736,6 +681,7 @@ protected function handleArguments(array $argv) * * @param string $loaderClass * @param string $loaderFile + * @return PHPUnit_Runner_TestSuiteLoader */ protected function handleLoader($loaderClass, $loaderFile = '') { @@ -746,11 +692,9 @@ protected function handleLoader($loaderClass, $loaderFile = '') ); } - $loaderFile = PHPUnit_Util_Filesystem::fileExistsInIncludePath( - $loaderFile - ); + $loaderFile = stream_resolve_include_path($loaderFile); - if ($loaderFile !== FALSE) { + if ($loaderFile) { require $loaderFile; } } @@ -777,23 +721,90 @@ protected function handleLoader($loaderClass, $loaderFile = '') 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 - * @param boolean $syntaxCheck + * @param string $filename */ - protected function handleBootstrap($filename, $syntaxCheck = FALSE) + protected function handleBootstrap($filename) { try { - PHPUnit_Util_Fileloader::checkAndLoad($filename, $syntaxCheck); + PHPUnit_Util_Fileloader::checkAndLoad($filename); } - catch (RuntimeException $e) { + 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. * @@ -825,21 +836,27 @@ protected function showHelp() --log-junit Log test execution in JUnit XML format to file. --log-tap Log test execution in TAP format to file. - --log-dbus Log test execution to DBUS. --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-clover Write code coverage data in Clover XML 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. @@ -851,17 +868,13 @@ protected function showHelp() --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 Mark a test as incomplete if no assertions are made. - --verbose Output more verbose information. - --wait Waits for a keystroke after each test. - - --skeleton-class Generate Unit class for UnitTest in UnitTest.php. - --skeleton-test Generate UnitTest class for Unit in Unit.php. + --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. - --syntax-check Try to check source files for syntax errors. --bootstrap A "bootstrap" PHP file that is run before the tests. -c|--configuration Read configuration from XML file. @@ -869,11 +882,9 @@ protected function showHelp() --include-path Prepend PHP's include_path with given path(s). -d key[=value] Sets a php.ini value. - --help Prints this usage information. + -h|--help Prints this usage information. --version Prints the version and exits. - --debug Output debugging information. - EOT; } diff --git a/libs/PHPUnit/PHPUnit/TextUI/ResultPrinter.php b/libs/PHPUnit/PHPUnit/TextUI/ResultPrinter.php index 421a138..0ed0ed2 100644 --- a/libs/PHPUnit/PHPUnit/TextUI/ResultPrinter.php +++ b/libs/PHPUnit/PHPUnit/TextUI/ResultPrinter.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,23 +37,20 @@ * @package PHPUnit * @subpackage TextUI * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'PHP/Timer.php'; - /** * Prints the result of a TextUI TestRunner run. * * @package PHPUnit * @subpackage TextUI * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -121,7 +118,7 @@ class PHPUnit_TextUI_ResultPrinter extends PHPUnit_Util_Printer implements PHPUn * @param boolean $verbose * @param boolean $colors * @param boolean $debug - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.0.0 */ public function __construct($out = NULL, $verbose = FALSE, $colors = FALSE, $debug = FALSE) @@ -273,10 +270,21 @@ protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) $this->write( $defect->getExceptionAsString() . "\n" . PHPUnit_Util_Filter::getFilteredStacktrace( - $defect->thrownException(), - FALSE + $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(); + } } /** @@ -334,7 +342,21 @@ protected function printHeader() */ protected function printFooter(PHPUnit_Framework_TestResult $result) { - if ($result->wasSuccessful() && + 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) { @@ -423,7 +445,7 @@ protected function printFooter(PHPUnit_Framework_TestResult $result) if (!$this->verbose && $result->deprecatedFeaturesCount() > 0) { $message = sprintf( - "Warning: Deprecated PHPUnit features are being used %s times!\n". + "Warning: Deprecated PHPUnit features are being used %s times!\n" . "Use --verbose for more information.\n", $result->deprecatedFeaturesCount() ); @@ -475,7 +497,12 @@ public function printWaitPrompt() */ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { - $this->writeProgress('E'); + if ($this->colors) { + $this->writeProgress("\x1b[31;1mE\x1b[0m"); + } else { + $this->writeProgress('E'); + } + $this->lastTestFailed = TRUE; } @@ -488,7 +515,12 @@ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) */ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { - $this->writeProgress('F'); + if ($this->colors) { + $this->writeProgress("\x1b[41;37mF\x1b[0m"); + } else { + $this->writeProgress('F'); + } + $this->lastTestFailed = TRUE; } @@ -501,7 +533,12 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser */ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { - $this->writeProgress('I'); + if ($this->colors) { + $this->writeProgress("\x1b[33;1mI\x1b[0m"); + } else { + $this->writeProgress('I'); + } + $this->lastTestFailed = TRUE; } @@ -515,7 +552,12 @@ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $t */ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { - $this->writeProgress('S'); + if ($this->colors) { + $this->writeProgress("\x1b[36;1mS\x1b[0m"); + } else { + $this->writeProgress('S'); + } + $this->lastTestFailed = TRUE; } @@ -576,7 +618,17 @@ public function endTest(PHPUnit_Framework_Test $test, $time) $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()); + } + } } /** diff --git a/libs/PHPUnit/PHPUnit/TextUI/TestRunner.php b/libs/PHPUnit/PHPUnit/TextUI/TestRunner.php index a5572d2..803392a 100644 --- a/libs/PHPUnit/PHPUnit/TextUI/TestRunner.php +++ b/libs/PHPUnit/PHPUnit/TextUI/TestRunner.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage TextUI * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @package PHPUnit * @subpackage TextUI * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -63,33 +62,44 @@ class PHPUnit_TextUI_TestRunner extends PHPUnit_Runner_BaseTestRunner const EXCEPTION_EXIT = 2; /** - * @var PHPUnit_Runner_TestSuiteLoader + * @var PHP_CodeCoverage_Filter + */ + protected $codeCoverageFilter; + + /** + * @var PHPUnit_Runner_TestSuiteLoader */ protected $loader = NULL; /** - * @var PHPUnit_TextUI_ResultPrinter + * @var PHPUnit_TextUI_ResultPrinter */ protected $printer = NULL; /** - * @var boolean + * @var boolean */ protected static $versionStringPrinted = FALSE; /** - * @param PHPUnit_Runner_TestSuiteLoader $loader - * @since Method available since Release 3.4.0 + * @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) + public function __construct(PHPUnit_Runner_TestSuiteLoader $loader = NULL, PHP_CodeCoverage_Filter $filter = NULL) { - $this->loader = $loader; + if ($filter === NULL) { + $filter = new PHP_CodeCoverage_Filter; + } + + $this->codeCoverageFilter = $filter; + $this->loader = $loader; } /** * @param mixed $test * @param array $arguments - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public static function run($test, array $arguments = array()) { @@ -105,30 +115,12 @@ public static function run($test, array $arguments = array()) $arguments ); } else { - throw new InvalidArgumentException( + throw new PHPUnit_Framework_Exception( 'No test case or test suite found.' ); } } - /** - * Runs a single test and waits until the user types RETURN. - * - * @param PHPUnit_Framework_Test $suite - */ - public static function runAndWait(PHPUnit_Framework_Test $suite) - { - $aTestRunner = new PHPUnit_TextUI_TestRunner; - - $aTestRunner->doRun( - $suite, - array( - 'wait' => TRUE - ) - ); - - } - /** * @return PHPUnit_Framework_TestResult */ @@ -218,6 +210,15 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) $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) { @@ -230,22 +231,6 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) $result->addListener(new PHPUnit_Util_DeprecatedFeature_Logger); } - if (isset($arguments['storyHTMLFile'])) { - $result->addListener( - new PHPUnit_Extensions_Story_ResultPrinter_HTML( - $arguments['storyHTMLFile'] - ) - ); - } - - if (isset($arguments['storyTextFile'])) { - $result->addListener( - new PHPUnit_Extensions_Story_ResultPrinter_Text( - $arguments['storyTextFile'] - ) - ); - } - if (isset($arguments['testdoxHTMLFile'])) { $result->addListener( new PHPUnit_Util_TestDox_ResultPrinter_HTML( @@ -262,14 +247,58 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) ); } - if ((isset($arguments['coverageClover']) || - isset($arguments['reportDirectory'])) && - extension_loaded('xdebug')) { - $result->collectCodeCoverageInformation(TRUE); + $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 (isset($arguments['logDbus'])) { - $result->addListener(new PHPUnit_Util_Log_DBUS); + if ($codeCoverageReports > 1) { + if (isset($arguments['cacheTokens'])) { + $codeCoverage->setCacheTokens($arguments['cacheTokens']); + } } if (isset($arguments['jsonLogfile'])) { @@ -294,6 +323,18 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) if ($arguments['strict']) { $result->strictMode(TRUE); + + $result->setTimeoutForSmallTests( + $arguments['timeoutForSmallTests'] + ); + + $result->setTimeoutForMediumTests( + $arguments['timeoutForMediumTests'] + ); + + $result->setTimeoutForLargeTests( + $arguments['timeoutForLargeTests'] + ); } $suite->run( @@ -311,81 +352,74 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) $this->printer->printResult($result); } - if (extension_loaded('tokenizer') && extension_loaded('xdebug')) { + if (isset($codeCoverage)) { if (isset($arguments['coverageClover'])) { $this->printer->write( - "\nWriting code coverage data to XML file, this may take " . - 'a moment.' + "\nGenerating code coverage report in Clover XML format ..." ); - require_once 'PHP/CodeCoverage/Report/Clover.php'; - $writer = new PHP_CodeCoverage_Report_Clover; - $writer->process( - $result->getCodeCoverage(), $arguments['coverageClover'] - ); + $writer->process($codeCoverage, $arguments['coverageClover']); - $this->printer->write("\n"); + $this->printer->write(" done\n"); unset($writer); } if (isset($arguments['reportDirectory'])) { $this->printer->write( - "\nGenerating code coverage report, this may take a moment." + "\nGenerating code coverage report in HTML format ..." ); - $title = ''; - - if (isset($arguments['configuration'])) { - $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration(); - - if (isset($loggingConfiguration['title'])) { - $title = $loggingConfiguration['title']; - } - } - - require_once 'PHP/CodeCoverage/Report/HTML.php'; - $writer = new PHP_CodeCoverage_Report_HTML( - array( - 'title' => $title, - 'charset' => $arguments['reportCharset'], - 'yui' => $arguments['reportYUI'], - 'highlight' => $arguments['reportHighlight'], - 'lowUpperBound' => $arguments['reportLowUpperBound'], - 'highLowerBound' => $arguments['reportHighLowerBound'], - 'generator' => ' and PHPUnit ' . PHPUnit_Runner_Version::id() + $arguments['reportCharset'], + $arguments['reportHighlight'], + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + sprintf( + ' and PHPUnit %s', + PHPUnit_Runner_Version::id() ) ); - $writer->process( - $result->getCodeCoverage(), $arguments['reportDirectory'] - ); + $writer->process($codeCoverage, $arguments['reportDirectory']); - $this->printer->write("\n"); + $this->printer->write(" done\n"); unset($writer); } - } - $this->pause($arguments['wait']); + if (isset($arguments['coveragePHP'])) { + $this->printer->write( + "\nGenerating code coverage report in PHP format ..." + ); - return $result; - } + $writer = new PHP_CodeCoverage_Report_PHP; + $writer->process($codeCoverage, $arguments['coveragePHP']); - /** - * @param boolean $wait - */ - protected function pause($wait) - { - if (!$wait) { - return; - } + $this->printer->write(" done\n"); + unset($writer); + } - if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) { - $this->printer->printWaitPrompt(); + 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); + } } - fgets(STDIN); + return $result; } /** @@ -405,7 +439,7 @@ public function setPrinter(PHPUnit_TextUI_ResultPrinter $resultPrinter) protected function runFailed($message) { self::printVersionString(); - self::write($message); + self::write($message . PHP_EOL); exit(self::FAILURE_EXIT); } @@ -473,7 +507,6 @@ protected function handleConfiguration(array &$arguments) $arguments['debug'] = isset($arguments['debug']) ? $arguments['debug'] : FALSE; $arguments['filter'] = isset($arguments['filter']) ? $arguments['filter'] : FALSE; $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - $arguments['wait'] = isset($arguments['wait']) ? $arguments['wait'] : FALSE; if (isset($arguments['configuration'])) { $arguments['configuration']->handlePHPConfiguration(); @@ -495,6 +528,11 @@ protected function handleConfiguration(array &$arguments) $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']; @@ -525,6 +563,21 @@ protected function handleConfiguration(array &$arguments) $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']; @@ -545,6 +598,11 @@ protected function handleConfiguration(array &$arguments) $arguments['mapTestClassNameToCoveredClassName'] = $phpunitConfiguration['mapTestClassNameToCoveredClassName']; } + $groupCliArgs = array(); + if (!empty($arguments['groups'])) { + $groupCliArgs = $arguments['groups']; + } + $groupConfiguration = $arguments['configuration']->getGroupConfiguration(); if (!empty($groupConfiguration['include']) && @@ -554,22 +612,16 @@ protected function handleConfiguration(array &$arguments) if (!empty($groupConfiguration['exclude']) && !isset($arguments['excludeGroups'])) { - $arguments['excludeGroups'] = $groupConfiguration['exclude']; + $arguments['excludeGroups'] = array_diff($groupConfiguration['exclude'], $groupCliArgs); } foreach ($arguments['configuration']->getListenerConfiguration() as $listener) { if (!class_exists($listener['class'], FALSE) && $listener['file'] !== '') { - $file = PHPUnit_Util_Filesystem::fileExistsInIncludePath( - $listener['file'] - ); - - if ($file !== FALSE) { - require $file; - } + require_once $listener['file']; } - if (class_exists($listener['class'], FALSE)) { + if (class_exists($listener['class'])) { if (count($listener['arguments']) == 0) { $listener = new $listener['class']; } else { @@ -596,11 +648,6 @@ protected function handleConfiguration(array &$arguments) $arguments['reportCharset'] = $loggingConfiguration['charset']; } - if (isset($loggingConfiguration['yui']) && - !isset($arguments['reportYUI'])) { - $arguments['reportYUI'] = $loggingConfiguration['yui']; - } - if (isset($loggingConfiguration['highlight']) && !isset($arguments['reportHighlight'])) { $arguments['reportHighlight'] = $loggingConfiguration['highlight']; @@ -624,6 +671,21 @@ protected function handleConfiguration(array &$arguments) $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']; @@ -650,16 +712,6 @@ protected function handleConfiguration(array &$arguments) } } - if (isset($loggingConfiguration['story-html']) && - !isset($arguments['storyHTMLFile'])) { - $arguments['storyHTMLFile'] = $loggingConfiguration['story-html']; - } - - if (isset($loggingConfiguration['story-text']) && - !isset($arguments['storyTextFile'])) { - $arguments['storsTextFile'] = $loggingConfiguration['story-text']; - } - if (isset($loggingConfiguration['testdox-html']) && !isset($arguments['testdoxHTMLFile'])) { $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html']; @@ -669,98 +721,86 @@ protected function handleConfiguration(array &$arguments) !isset($arguments['testdoxTextFile'])) { $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text']; } - } - - if (isset($arguments['configuration'])) { - $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); - - $filter = PHP_CodeCoverage_Filter::getInstance(); - - foreach ($filterConfiguration['blacklist']['include']['directory'] as $dir) { - $filter->addDirectoryToBlacklist( - $dir['path'], $dir['suffix'], $dir['prefix'], $dir['group'] - ); - } - - foreach ($filterConfiguration['blacklist']['include']['file'] as $file) { - $filter->addFileToBlacklist($file); - } - - foreach ($filterConfiguration['blacklist']['exclude']['directory'] as $dir) { - $filter->removeDirectoryFromBlacklist( - $dir['path'], $dir['suffix'], $dir['prefix'], $dir['group'] - ); - } - - foreach ($filterConfiguration['blacklist']['exclude']['file'] as $file) { - $filter->removeFileFromBlacklist($file); - } if ((isset($arguments['coverageClover']) || - isset($arguments['reportDirectory'])) && + isset($arguments['reportDirectory']) || + isset($arguments['coveragePHP']) || + isset($arguments['coverageText'])) && extension_loaded('xdebug')) { - $coverage = PHP_CodeCoverage::getInstance(); - $coverage->setProcessUncoveredFilesFromWhitelist( - $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist'] - ); + $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); + $arguments['addUncoveredFilesFromWhitelist'] = $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']; + $arguments['processUncoveredFilesFromWhitelist'] = $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']; - if (isset($arguments['forceCoversAnnotation'])) { - $coverage->setForceCoversAnnotation( - $arguments['forceCoversAnnotation'] + foreach ($filterConfiguration['blacklist']['include']['directory'] as $dir) { + $this->codeCoverageFilter->addDirectoryToBlacklist( + $dir['path'], $dir['suffix'], $dir['prefix'], $dir['group'] ); } - if (isset($arguments['mapTestClassNameToCoveredClassName'])) { - $coverage->setMapTestClassNameToCoveredClassName( - $arguments['mapTestClassNameToCoveredClassName'] + 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) { - $filter->addDirectoryToWhitelist( + $this->codeCoverageFilter->addDirectoryToWhitelist( $dir['path'], $dir['suffix'], $dir['prefix'] ); } foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { - $filter->addFileToWhitelist($file); + $this->codeCoverageFilter->addFileToWhitelist($file); } foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { - $filter->removeDirectoryFromWhitelist( + $this->codeCoverageFilter->removeDirectoryFromWhitelist( $dir['path'], $dir['suffix'], $dir['prefix'] ); } foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { - $filter->removeFileFromWhitelist($file); + $this->codeCoverageFilter->removeFileFromWhitelist($file); } } } - $arguments['backupGlobals'] = isset($arguments['backupGlobals']) ? $arguments['backupGlobals'] : NULL; - $arguments['backupStaticAttributes'] = isset($arguments['backupStaticAttributes']) ? $arguments['backupStaticAttributes'] : NULL; - $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['reportYUI'] = isset($arguments['reportYUI']) ? $arguments['reportYUI'] : TRUE; - $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['strict'] = isset($arguments['strict']) ? $arguments['strict'] : FALSE; - $arguments['verbose'] = isset($arguments['verbose']) ? $arguments['verbose'] : FALSE; + $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'])) { diff --git a/libs/PHPUnit/PHPUnit/Util/Class.php b/libs/PHPUnit/PHPUnit/Util/Class.php index f31777e..85ac032 100644 --- a/libs/PHPUnit/PHPUnit/Util/Class.php +++ b/libs/PHPUnit/PHPUnit/Util/Class.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,25 +37,20 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ -if (!defined('T_NAMESPACE')) { - define('T_NAMESPACE', 377); -} - /** * Class helpers. * * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -142,17 +137,28 @@ public static function getMethodParameters($method, $forCall = FALSE) foreach ($method->getParameters() as $i => $parameter) { $name = '$' . $parameter->getName(); - if ($name === '$') { - $name .= 'arg' . $i; + /* 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 = ''; - $typeHint = ''; + $default = ''; + $reference = ''; + $typeHint = ''; if (!$forCall) { if ($parameter->isArray()) { $typeHint = 'array '; - } else { + } + + else if (version_compare(PHP_VERSION, '5.4', '>') && + $parameter->isCallable()) { + $typeHint = 'callable '; + } + + else { try { $class = $parameter->getClass(); } @@ -174,15 +180,13 @@ public static function getMethodParameters($method, $forCall = FALSE) else if ($parameter->isOptional()) { $default = ' = null'; } - } - $ref = ''; - - if ($parameter->isPassedByReference()) { - $ref = '&'; + if ($parameter->isPassedByReference()) { + $reference = '&'; + } } - $parameters[] = $typeHint . $ref . $name . $default; + $parameters[] = $typeHint . $reference . $name . $default; } return join(', ', $parameters); @@ -241,7 +245,7 @@ public static function getPackageInformation($className, $docComment) * @param string $className * @param string $attributeName * @return mixed - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.4.0 */ public static function getStaticAttribute($className, $attributeName) @@ -286,7 +290,7 @@ public static function getStaticAttribute($className, $attributeName) * @param object $object * @param string $attributeName * @return mixed - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.4.0 */ public static function getObjectAttribute($object, $attributeName) @@ -318,66 +322,24 @@ public static function getObjectAttribute($object, $attributeName) } if (isset($attribute)) { - if ($attribute == NULL || $attribute->isPublic()) { + if (!$attribute || $attribute->isPublic()) { return $object->$attributeName; - } else { - $array = (array)$object; - $protectedName = "\0*\0" . $attributeName; - - if (array_key_exists($protectedName, $array)) { - return $array[$protectedName]; - } else { - $classes = self::getHierarchy(get_class($object)); - - foreach ($classes as $class) { - $privateName = sprintf( - "\0%s\0%s", - - $class, - $attributeName - ); - - if (array_key_exists($privateName, $array)) { - return $array[$privateName]; - } - } - } } + $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 ) ); } - /** - * - * - * @param string $className - * @return array - * @since Method available since Release 3.4.0 - */ - public static function parseFullyQualifiedClassName($className) - { - $result = array( - 'namespace' => '', - 'className' => $className, - 'fullyQualifiedClassName' => $className - ); - - if (strpos($className, '\\') !== FALSE) { - $tmp = explode('\\', $className); - $result['className'] = $tmp[count($tmp)-1]; - $result['namespace'] = self::arrayToName($tmp); - } - - return $result; - } - /** * Returns the package information of a user-defined class. * diff --git a/libs/PHPUnit/PHPUnit/Util/Configuration.php b/libs/PHPUnit/PHPUnit/Util/Configuration.php index fbf16c7..bd82673 100644 --- a/libs/PHPUnit/PHPUnit/Util/Configuration.php +++ b/libs/PHPUnit/PHPUnit/Util/Configuration.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -53,25 +53,30 @@ * * * - * /path/to/files - * /path/to/MyTest.php + * /path/to/files + * /path/to/MyTest.php + * /path/to/files/exclude * * * @@ -93,7 +98,8 @@ * /path/to/file * * - * + * * /path/to/files * /path/to/file * @@ -123,16 +129,14 @@ * * * - * * * * * * - * - * * * * @@ -164,9 +168,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -186,7 +189,7 @@ class PHPUnit_Util_Configuration protected function __construct($filename) { $this->filename = $filename; - $this->document = PHPUnit_Util_XML::loadFile($filename); + $this->document = PHPUnit_Util_XML::loadFile($filename, FALSE, TRUE); $this->xpath = new DOMXPath($this->document); } @@ -224,6 +227,17 @@ public static function getInstance($filename) 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. * @@ -232,16 +246,29 @@ public static function getInstance($filename) */ public function getFilterConfiguration() { - $addUncoveredFilesFromWhitelist = TRUE; + $addUncoveredFilesFromWhitelist = TRUE; + $processUncoveredFilesFromWhitelist = FALSE; $tmp = $this->xpath->query('filter/whitelist'); - if ($tmp->length == 1 && - $tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { - $addUncoveredFilesFromWhitelist = $this->getBoolean( - (string)$tmp->item(0)->getAttribute('addUncoveredFilesFromWhitelist'), - TRUE - ); + 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( @@ -265,6 +292,7 @@ public function getFilterConfiguration() ), 'whitelist' => array( 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, + 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, 'include' => array( 'directory' => $this->readFilterDirectories( 'filter/whitelist/directory' @@ -325,20 +353,24 @@ public function getListenerConfiguration() $arguments = array(); if ($listener->hasAttribute('file')) { - $file = $this->toAbsolutePath((string)$listener->getAttribute('file')); + $file = $this->toAbsolutePath( + (string)$listener->getAttribute('file'), TRUE + ); } - if ($listener->childNodes->item(1) instanceof DOMElement && - $listener->childNodes->item(1)->tagName == 'arguments') { - foreach ($listener->childNodes->item(1)->childNodes as $argument) { + 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') { + if ($argument->tagName == 'file' || + $argument->tagName == 'directory') { $arguments[] = $this->toAbsolutePath((string)$argument->nodeValue); } else { $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument); } } } + } } $result[] = array( @@ -361,8 +393,11 @@ 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')); + $type = (string)$log->getAttribute('type'); + + $target = $this->toAbsolutePath( + (string)$log->getAttribute('target') + ); if ($type == 'coverage-html') { if ($log->hasAttribute('title')) { @@ -381,13 +416,6 @@ public function getLoggingConfiguration() $result['highLowerBound'] = (string)$log->getAttribute('highLowerBound'); } - if ($log->hasAttribute('yui')) { - $result['yui'] = $this->getBoolean( - (string)$log->getAttribute('yui'), - FALSE - ); - } - if ($log->hasAttribute('highlight')) { $result['highlight'] = $this->getBoolean( (string)$log->getAttribute('highlight'), @@ -405,6 +433,15 @@ public function getLoggingConfiguration() } } + else if ($type == 'coverage-text') { + if ($log->hasAttribute('showUncoveredFiles')) { + $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( + (string)$log->getAttribute('showUncoveredFiles'), + FALSE + ); + } + } + $result[$type] = $target; } @@ -420,7 +457,7 @@ public function getLoggingConfiguration() public function getPHPConfiguration() { $result = array( - 'include_path' => '', + 'include_path' => array(), 'ini' => array(), 'const' => array(), 'var' => array(), @@ -433,10 +470,10 @@ public function getPHPConfiguration() 'request' => array() ); - $nl = $this->xpath->query('php/includePath'); + foreach ($this->xpath->query('php/includePath') as $includePath) { + $path = (string)$includePath->nodeValue; - if ($nl->length == 1) { - $result['include_path'] = $this->toAbsolutePath((string)$nl->item(0)->nodeValue); + $result['include_path'][] = $this->toAbsolutePath($path); } foreach ($this->xpath->query('php/ini') as $ini) { @@ -474,10 +511,11 @@ public function handlePHPConfiguration() { $configuration = $this->getPHPConfiguration(); - if ($configuration['include_path'] != '') { + if (! empty($configuration['include_path'])) { ini_set( 'include_path', - $configuration['include_path'] . PATH_SEPARATOR . + implode(PATH_SEPARATOR, $configuration['include_path']) . + PATH_SEPARATOR . ini_get('include_path') ); } @@ -507,6 +545,10 @@ public function handlePHPConfiguration() $target[$name] = $value; } } + + foreach ($configuration['env'] as $name => $value) { + putenv("$name=$value"); + } } /** @@ -520,6 +562,12 @@ 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 @@ -605,12 +653,6 @@ public function getPHPUnitConfiguration() ); } - if ($root->hasAttribute('syntaxCheck')) { - $result['syntaxCheck'] = $this->getBoolean( - (string)$root->getAttribute('syntaxCheck'), FALSE - ); - } - if ($root->hasAttribute('testSuiteLoaderClass')) { $result['testSuiteLoaderClass'] = (string)$root->getAttribute( 'testSuiteLoaderClass' @@ -623,6 +665,36 @@ public function getPHPUnitConfiguration() ); } + 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 @@ -659,13 +731,17 @@ public function getSeleniumBrowserConfiguration() } if ($config->hasAttribute('port')) { - $port = (int)$config->getAttribute('port'); + $port = $this->getInteger( + (string)$config->getAttribute('port'), 4444 + ); } else { $port = 4444; } if ($config->hasAttribute('timeout')) { - $timeout = (int)$config->getAttribute('timeout'); + $timeout = $this->getInteger( + (string)$config->getAttribute('timeout'), 30000 + ); } else { $timeout = 30000; } @@ -685,11 +761,10 @@ public function getSeleniumBrowserConfiguration() /** * Returns the test suite configuration. * - * @param boolean $syntaxCheck * @return PHPUnit_Framework_TestSuite * @since Method available since Release 3.2.1 */ - public function getTestSuiteConfiguration($syntaxCheck = FALSE) + public function getTestSuiteConfiguration($testSuiteFilter=null) { $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); @@ -698,7 +773,7 @@ public function getTestSuiteConfiguration($syntaxCheck = FALSE) } if ($testSuiteNodes->length == 1) { - return $this->getTestSuite($testSuiteNodes->item(0), $syntaxCheck); + return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); } if ($testSuiteNodes->length > 1) { @@ -706,7 +781,7 @@ public function getTestSuiteConfiguration($syntaxCheck = FALSE) foreach ($testSuiteNodes as $testSuiteNode) { $suite->addTestSuite( - $this->getTestSuite($testSuiteNode, $syntaxCheck) + $this->getTestSuite($testSuiteNode, $testSuiteFilter) ); } @@ -716,11 +791,10 @@ public function getTestSuiteConfiguration($syntaxCheck = FALSE) /** * @param DOMElement $testSuiteNode - * @param boolean $syntaxCheck * @return PHPUnit_Framework_TestSuite * @since Method available since Release 3.4.0 */ - protected function getTestSuite(DOMElement $testSuiteNode, $syntaxCheck) + protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter=null) { if ($testSuiteNode->hasAttribute('name')) { $suite = new PHPUnit_Framework_TestSuite( @@ -730,13 +804,41 @@ protected function getTestSuite(DOMElement $testSuiteNode, $syntaxCheck) $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 { @@ -749,21 +851,52 @@ protected function getTestSuite(DOMElement $testSuiteNode, $syntaxCheck) $suffix = 'Test.php'; } - $testCollector = new PHPUnit_Runner_IncludePathTestCollector( - array($this->toAbsolutePath($directory)), $suffix, $prefix + $files = $fileIteratorFacade->getFilesAsArray( + $this->toAbsolutePath($directory), + $suffix, + $prefix, + $exclude ); - - $suite->addTestFiles($testCollector->collectTests(), $syntaxCheck); + $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; } - $suite->addTestFile($file, $syntaxCheck); + // 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; @@ -788,6 +921,21 @@ protected function getBoolean($value, $default) 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 @@ -844,19 +992,35 @@ protected function readFilterFiles($query) } /** - * @param string $path + * @param string $path + * @param boolean $useIncludePath * @return string * @since Method available since Release 3.5.0 */ - protected function toAbsolutePath($path) + protected function toAbsolutePath($path, $useIncludePath = FALSE) { - // is the path already an absolute path? + // 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; } - return dirname($this->filename) . DIRECTORY_SEPARATOR . $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/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature.php b/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature.php index 0b71c3f..ee3f032 100644 --- a/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature.php +++ b/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature.php @@ -39,7 +39,7 @@ * @author Ralph Schindler * @author Sebastian Bergmann * @copyright 2002-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,8 +52,7 @@ * @author Ralph Schindler * @author Sebastian Bergmann * @copyright 2002-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature/Logger.php b/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature/Logger.php index dca3c4c..721bdff 100644 --- a/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature/Logger.php +++ b/libs/PHPUnit/PHPUnit/Util/DeprecatedFeature/Logger.php @@ -39,7 +39,7 @@ * @author Ralph Schindler * @author Sebastian Bergmann * @copyright 2002-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -52,8 +52,7 @@ * @author Ralph Schindler * @author Sebastian Bergmann * @copyright 2002-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/Diff.php b/libs/PHPUnit/PHPUnit/Util/Diff.php index 338099c..b8b2f37 100644 --- a/libs/PHPUnit/PHPUnit/Util/Diff.php +++ b/libs/PHPUnit/PHPUnit/Util/Diff.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,8 @@ * @subpackage Util * @author Sebastian Bergmann * @author Kore Nordmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -51,16 +51,15 @@ * @subpackage Util * @author Sebastian Bergmann * @author Kore Nordmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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. + * Returns the diff between two arrays or strings as string. * * @param array|string $from * @param array|string $to @@ -68,6 +67,89 @@ class PHPUnit_Util_Diff */ 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); } @@ -76,7 +158,6 @@ public static function diff($from, $to) $to = preg_split('(\r\n|\r|\n)', $to); } - $buffer = "--- Expected\n+++ Actual\n"; $start = array(); $end = array(); $fromLength = count($from); @@ -110,6 +191,14 @@ public static function diff($from, $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 */); } @@ -144,65 +233,7 @@ public static function diff($from, $to) $diff[] = array($token, 0 /* OLD */); } - $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) { - // TODO: Implement chunk range information. - $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; + return $diff; } /** diff --git a/libs/PHPUnit/PHPUnit/Util/ErrorHandler.php b/libs/PHPUnit/PHPUnit/Util/ErrorHandler.php index 9342ff8..1299b36 100644 --- a/libs/PHPUnit/PHPUnit/Util/ErrorHandler.php +++ b/libs/PHPUnit/PHPUnit/Util/ErrorHandler.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -48,6 +48,7 @@ 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. @@ -55,9 +56,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -115,10 +115,18 @@ public static function handleError($errno, $errstr, $errfile, $errline) $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, $trace); + throw new $exception($errstr, $errno, $errfile, $errline); } } diff --git a/libs/PHPUnit/PHPUnit/Util/File.php b/libs/PHPUnit/PHPUnit/Util/File.php deleted file mode 100644 index b4bf1b4..0000000 --- a/libs/PHPUnit/PHPUnit/Util/File.php +++ /dev/null @@ -1,310 +0,0 @@ -. - * 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 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.4.0 - */ - -if (!defined('T_NAMESPACE')) { - define('T_NAMESPACE', 377); -} - -/** - * File helpers. - * - * @package PHPUnit - * @subpackage Util - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.4.0 - */ -class PHPUnit_Util_File -{ - /** - * @var array - */ - protected static $cache = array(); - - /** - * Returns information on the classes declared in a sourcefile. - * - * @param string $filename - * @return array - */ - public static function getClassesInFile($filename) - { - if (!isset(self::$cache[$filename])) { - self::$cache[$filename] = self::parseFile($filename); - } - - return self::$cache[$filename]; - } - - /** - * Parses a file for class and method information. - * - * @param string $filename - * @return array - */ - protected static function parseFile($filename) - { - $result = array(); - - $tokens = token_get_all( - file_get_contents($filename) - ); - $numTokens = count($tokens); - $blocks = array(); - $line = 1; - $currentBlock = FALSE; - $currentNamespace = FALSE; - $currentClass = FALSE; - $currentFunction = FALSE; - $currentFunctionStartLine = FALSE; - $currentFunctionTokens = array(); - $currentDocComment = FALSE; - $currentSignature = FALSE; - $currentSignatureStartToken = FALSE; - - for ($i = 0; $i < $numTokens; $i++) { - if ($currentFunction !== FALSE) { - $currentFunctionTokens[] = $tokens[$i]; - } - - if (is_string($tokens[$i])) { - if ($tokens[$i] == '{') { - if ($currentBlock == T_CLASS) { - $block = $currentClass; - } - - else if ($currentBlock == T_FUNCTION) { - $currentSignature = ''; - - for ($j = $currentSignatureStartToken; $j < $i; $j++) { - if (is_string($tokens[$j])) { - $currentSignature .= $tokens[$j]; - } else { - $currentSignature .= $tokens[$j][1]; - } - } - - $currentSignature = trim($currentSignature); - - $block = $currentFunction; - $currentSignatureStartToken = FALSE; - } - - else { - $block = FALSE; - } - - array_push($blocks, $block); - - $currentBlock = FALSE; - } - - else if ($tokens[$i] == '}') { - $block = array_pop($blocks); - - if ($block !== FALSE && $block !== NULL) { - if ($block == $currentFunction) { - if ($currentDocComment !== FALSE) { - $docComment = $currentDocComment; - $currentDocComment = FALSE; - } else { - $docComment = ''; - } - - $tmp = array( - 'docComment' => $docComment, - 'signature' => $currentSignature, - 'startLine' => $currentFunctionStartLine, - 'endLine' => $line, - 'tokens' => $currentFunctionTokens - ); - - if ($currentClass !== FALSE) { - $result[$currentClass]['methods'][$currentFunction] = $tmp; - } - - $currentFunction = FALSE; - $currentFunctionStartLine = FALSE; - $currentFunctionTokens = array(); - $currentSignature = FALSE; - } - - else if ($block == $currentClass) { - $result[$currentClass]['endLine'] = $line; - - $currentClass = FALSE; - $currentClassStartLine = FALSE; - } - } - } - - continue; - } - - switch ($tokens[$i][0]) { - case T_HALT_COMPILER: { - return; - } - break; - - case T_NAMESPACE: { - $currentNamespace = $tokens[$i+2][1]; - - for ($j = $i+3; $j < $numTokens; $j += 2) { - if ($tokens[$j][0] == T_NS_SEPARATOR) { - $currentNamespace .= '\\' . $tokens[$j+1][1]; - } else { - break; - } - } - } - break; - - case T_CURLY_OPEN: { - $currentBlock = T_CURLY_OPEN; - array_push($blocks, $currentBlock); - } - break; - - case T_DOLLAR_OPEN_CURLY_BRACES: { - $currentBlock = T_DOLLAR_OPEN_CURLY_BRACES; - array_push($blocks, $currentBlock); - } - break; - - case T_CLASS: { - $currentBlock = T_CLASS; - - if ($currentNamespace === FALSE) { - $currentClass = $tokens[$i+2][1]; - } else { - $currentClass = $currentNamespace . '\\' . - $tokens[$i+2][1]; - } - - if ($currentDocComment !== FALSE) { - $docComment = $currentDocComment; - $currentDocComment = FALSE; - } else { - $docComment = ''; - } - - $result[$currentClass] = array( - 'methods' => array(), - 'docComment' => $docComment, - 'startLine' => $line - ); - } - break; - - case T_FUNCTION: { - if (!((is_array($tokens[$i+2]) && - $tokens[$i+2][0] == T_STRING) || - (is_string($tokens[$i+2]) && - $tokens[$i+2] == '&' && - is_array($tokens[$i+3]) && - $tokens[$i+3][0] == T_STRING))) { - continue; - } - - $currentBlock = T_FUNCTION; - $currentFunctionStartLine = $line; - - $done = FALSE; - $currentSignatureStartToken = $i - 1; - - do { - switch ($tokens[$currentSignatureStartToken][0]) { - case T_ABSTRACT: - case T_FINAL: - case T_PRIVATE: - case T_PUBLIC: - case T_PROTECTED: - case T_STATIC: - case T_WHITESPACE: { - $currentSignatureStartToken--; - } - break; - - default: { - $currentSignatureStartToken++; - $done = TRUE; - } - } - } - while (!$done); - - if (isset($tokens[$i+2][1])) { - $functionName = $tokens[$i+2][1]; - } - - else if (isset($tokens[$i+3][1])) { - $functionName = $tokens[$i+3][1]; - } - - if ($currentNamespace === FALSE) { - $currentFunction = $functionName; - } else { - $currentFunction = $currentNamespace . '\\' . - $functionName; - } - } - break; - - case T_DOC_COMMENT: { - $currentDocComment = $tokens[$i][1]; - } - break; - } - - $line += substr_count($tokens[$i][1], "\n"); - } - - return $result; - } -} diff --git a/libs/PHPUnit/PHPUnit/Util/Fileloader.php b/libs/PHPUnit/PHPUnit/Util/Fileloader.php index 3bdaa6f..7eeb7df 100644 --- a/libs/PHPUnit/PHPUnit/Util/Fileloader.php +++ b/libs/PHPUnit/PHPUnit/Util/Fileloader.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,40 +49,30 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 and is optionally checked for - * syntax errors through the syntaxCheck() method. The sourcefile is - * loaded through the load() method. + * Checks if a PHP sourcefile is readable. + * The sourcefile is loaded through the load() method. * - * @param string $filename - * @param boolean $syntaxCheck - * @return string - * @throws RuntimeException + * @param string $filename + * @throws PHPUnit_Framework_Exception */ - public static function checkAndLoad($filename, $syntaxCheck = FALSE) + public static function checkAndLoad($filename) { - $includePathFilename = PHPUnit_Util_Filesystem::fileExistsInIncludePath( - $filename - ); + $includePathFilename = stream_resolve_include_path($filename); if (!$includePathFilename || !is_readable($includePathFilename)) { - throw new RuntimeException( + throw new PHPUnit_Framework_Exception( sprintf('Cannot open file "%s".' . "\n", $filename) ); } - if ($syntaxCheck) { - self::syntaxCheck($includePathFilename); - } - self::load($includePathFilename); return $includePathFilename; @@ -114,27 +104,4 @@ public static function load($filename) return $filename; } - - /** - * Uses a separate process to perform a syntax check on a PHP sourcefile. - * - * @param string $filename - * @throws RuntimeException - * @since Method available since Release 3.0.0 - */ - protected static function syntaxCheck($filename) - { - $command = PHPUnit_Util_PHP::getPhpBinary(); - - if (DIRECTORY_SEPARATOR == '\\') { - $command = escapeshellarg($command); - } - - $command .= ' -l ' . escapeshellarg($filename); - $output = shell_exec($command); - - if (strpos($output, 'Errors parsing') !== FALSE) { - throw new RuntimeException($output); - } - } } diff --git a/libs/PHPUnit/PHPUnit/Util/Filesystem.php b/libs/PHPUnit/PHPUnit/Util/Filesystem.php index 561626e..80cb119 100644 --- a/libs/PHPUnit/PHPUnit/Util/Filesystem.php +++ b/libs/PHPUnit/PHPUnit/Util/Filesystem.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -79,76 +78,4 @@ public static function classNameToFilename($className) $className ) . '.php'; } - - /** - * Starts the collection of loaded files. - * - * @since Method available since Release 3.3.0 - */ - public static function collectStart() - { - self::$buffer = get_included_files(); - } - - /** - * Stops the collection of loaded files and - * returns the names of the loaded files. - * - * @return array - * @since Method available since Release 3.3.0 - */ - public static function collectEnd() - { - return array_values( - array_diff(get_included_files(), self::$buffer) - ); - } - - /** - * Stops the collection of loaded files and adds - * the names of the loaded files to the blacklist. - * - * @return array - * @since Method available since Release 3.4.6 - */ - public static function collectEndAndAddToBlacklist() - { - foreach (self::collectEnd() as $blacklistedFile) { - PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist( - $blacklistedFile, 'PHPUNIT' - ); - } - } - - /** - * Implementation of stream_resolve_include_path() in PHP - * for version before PHP 5.3.2. - * - * @param string $file - * @return mixed - * @author Mattis Stordalen Flister - * @since Method available since Release 3.2.9 - */ - public static function fileExistsInIncludePath($file) - { - if (function_exists('stream_resolve_include_path')) { - return stream_resolve_include_path($file); - } - - if (file_exists($file)) { - return realpath($file); - } - - $paths = explode(PATH_SEPARATOR, get_include_path()); - - foreach ($paths as $path) { - $fullpath = $path . DIRECTORY_SEPARATOR . $file; - - if (file_exists($fullpath)) { - return realpath($fullpath); - } - } - - return FALSE; - } } diff --git a/libs/PHPUnit/PHPUnit/Util/Filter.php b/libs/PHPUnit/PHPUnit/Util/Filter.php index ab48ba8..3d8e4f5 100644 --- a/libs/PHPUnit/PHPUnit/Util/Filter.php +++ b/libs/PHPUnit/PHPUnit/Util/Filter.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,23 +37,20 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'File/Iterator/Factory.php'; - /** * Utility class for code filtering. * * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -63,44 +60,55 @@ class PHPUnit_Util_Filter * Filters stack frames from PHPUnit classes. * * @param Exception $e - * @param boolean $filterTests * @param boolean $asString * @return string */ - public static function getFilteredStacktrace(Exception $e, $filterTests = TRUE, $asString = TRUE) + public static function getFilteredStacktrace(Exception $e, $asString = TRUE) { - if ($asString === TRUE) { - $filteredStacktrace = ''; - } else { - $filteredStacktrace = array(); - } + $prefix = FALSE; + $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); - $groups = array('DEFAULT'); + if (substr($script, -5) == '.phar') { + $prefix = 'phar://' . $script . '/'; + } if (!defined('PHPUNIT_TESTSUITE')) { - $groups[] = 'PHPUNIT'; + $blacklist = PHPUnit_Util_GlobalState::phpunitFiles(); + } else { + $blacklist = array(); } - if ($filterTests) { - $groups[] = 'TESTS'; + if ($asString === TRUE) { + $filteredStacktrace = ''; + } else { + $filteredStacktrace = array(); } if ($e instanceof PHPUnit_Framework_SyntheticError) { $eTrace = $e->getSyntheticTrace(); + $eFile = $e->getSyntheticFile(); + $eLine = $e->getSyntheticLine(); } else { - $eTrace = $e->getTrace(); + if ($e->getPrevious()) { + $eTrace = $e->getPrevious()->getTrace(); + } else { + $eTrace = $e->getTrace(); + } + $eFile = $e->getFile(); + $eLine = $e->getLine(); } - if (!self::frameExists($eTrace, $e->getFile(), $e->getLine())) { + if (!self::frameExists($eTrace, $eFile, $eLine)) { array_unshift( - $eTrace, array('file' => $e->getFile(), 'line' => $e->getLine()) + $eTrace, array('file' => $eFile, 'line' => $eLine) ); } foreach ($eTrace as $frame) { if (isset($frame['file']) && is_file($frame['file']) && - !PHP_CodeCoverage::getInstance()->filter()->isFiltered( - $frame['file'], $groups, TRUE)) { + !isset($blacklist[$frame['file']]) && + strpos($frame['file'], $prefix) !== 0 && + $frame['file'] !== $script) { if ($asString === TRUE) { $filteredStacktrace .= sprintf( "%s:%s\n", diff --git a/libs/PHPUnit/PHPUnit/Util/Getopt.php b/libs/PHPUnit/PHPUnit/Util/Getopt.php index d04c888..263558a 100644 --- a/libs/PHPUnit/PHPUnit/Util/Getopt.php +++ b/libs/PHPUnit/PHPUnit/Util/Getopt.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -50,9 +50,8 @@ * @subpackage Util * @author Andrei Zmievski * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/GlobalState.php b/libs/PHPUnit/PHPUnit/Util/GlobalState.php index 1cacc5a..425f0d4 100644 --- a/libs/PHPUnit/PHPUnit/Util/GlobalState.php +++ b/libs/PHPUnit/PHPUnit/Util/GlobalState.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -92,6 +91,11 @@ class PHPUnit_Util_GlobalState 'HTTP_POST_FILES' ); + /** + * @var array + */ + protected static $phpunitFiles; + public static function backupGlobals(array $blacklist) { self::$globals = array(); @@ -106,7 +110,8 @@ public static function backupGlobals(array $blacklist) foreach (array_keys($GLOBALS) as $key) { if ($key != 'GLOBALS' && !in_array($key, $superGlobalArrays) && - !in_array($key, $blacklist)) { + !in_array($key, $blacklist) && + !$GLOBALS[$key] instanceof Closure) { self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]); } } @@ -184,14 +189,25 @@ protected static function restoreSuperGlobalArray($superGlobalArray) public static function getIncludedFilesAsString() { - $blacklist = PHP_CodeCoverage::getInstance()->filter()->getBlacklist(); - $blacklist = array_flip($blacklist['PHPUNIT']); + $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--) { - if (!isset($blacklist[$files[$i]]) && is_file($files[$i])) { - $result = 'require_once \'' . $files[$i] . "';\n" . $result; + $file = $files[$i]; + + if ($prefix !== FALSE) { + $file = str_replace($prefix, '', $file); + } + + if (!isset($blacklist[$file]) && is_file($file)) { + $result = 'require_once \'' . $file . "';\n" . $result; } } @@ -226,6 +242,10 @@ public static function getGlobalsAsString() 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, @@ -241,7 +261,7 @@ public static function getGlobalsAsString() $blacklist[] = '_PEAR_Config_instance'; foreach (array_keys($GLOBALS) as $key) { - if (!in_array($key, $blacklist)) { + if (!in_array($key, $blacklist) && !$GLOBALS[$key] instanceof Closure) { $result .= sprintf( '$GLOBALS[\'%s\'] = %s;' . "\n", $key, @@ -274,9 +294,10 @@ public static function backupStaticAttributes(array $blacklist) 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], 'sfYaml') !== 0 && + strpos($declaredClasses[$i], 'Symfony') !== 0 && strpos($declaredClasses[$i], 'Text_Template') !== 0 && !$declaredClasses[$i] instanceof PHPUnit_Framework_Test) { $class = new ReflectionClass($declaredClasses[$i]); @@ -294,7 +315,11 @@ public static function backupStaticAttributes(array $blacklist) if (!isset($blacklist[$declaredClasses[$i]]) || !in_array($name, $blacklist[$declaredClasses[$i]])) { $attribute->setAccessible(TRUE); - $backup[$name] = serialize($attribute->getValue()); + $value = $attribute->getValue(); + + if (!$value instanceof Closure) { + $backup[$name] = serialize($value); + } } } } @@ -351,4 +376,52 @@ protected static function arrayOnlyContainsScalars(array $array) 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/libs/PHPUnit/PHPUnit/Util/InvalidArgumentHelper.php b/libs/PHPUnit/PHPUnit/Util/InvalidArgumentHelper.php index 1465f30..b5212e8 100644 --- a/libs/PHPUnit/PHPUnit/Util/InvalidArgumentHelper.php +++ b/libs/PHPUnit/PHPUnit/Util/InvalidArgumentHelper.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,21 +37,21 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 InvalidArgumentException objects. + * 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 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -66,7 +66,7 @@ public static function factory($argument, $type, $value = NULL) { $stack = debug_backtrace(FALSE); - return new InvalidArgumentException( + return new PHPUnit_Framework_Exception( sprintf( 'Argument #%d%sof %s::%s() must be a %s', $argument, diff --git a/libs/PHPUnit/PHPUnit/Util/Log/DBUS.php b/libs/PHPUnit/PHPUnit/Util/Log/DBUS.php deleted file mode 100644 index 83464a7..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Log/DBUS.php +++ /dev/null @@ -1,236 +0,0 @@ -. - * 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 Benjamin Eberlei - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.5.0 - */ - -/** - * A TestListener that integrates with DBUS. - * - * @package PHPUnit - * @subpackage Util_Log - * @author Benjamin Eberlei - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.5.0 - */ -class PHPUnit_Util_Log_DBUS implements PHPUnit_Framework_TestListener -{ - /** - * @var integer - */ - protected $errors = 0; - - /** - * @var integer - */ - protected $failures = 0; - - /** - * @var integer - */ - protected $startTime = NULL; - - /** - * @var string - */ - protected $suiteName = ''; - - /** - * @var integer - */ - protected $tests = 0; - - /** - * @var integer - */ - protected $startedSuites = 0; - - /** - * @var integer - */ - protected $endedSuites = 0; - - /** - * - */ - public function __construct() - { - if (!extension_loaded('dbus')) { - throw new RuntimeException('ext/dbus is not available'); - } - } - - /** - * 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->errors++; - } - - /** - * 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->failures++; - } - - /** - * 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) - { - if($this->startedSuites == 0) { - $this->startTime = time(); - $this->suiteName = $suite->getName(); - } - - $this->startedSuites++; - } - - /** - * A test suite ended. - * - * @param PHPUnit_Framework_TestSuite $suite - * @since Method available since Release 2.2.0 - */ - public function endTestSuite(PHPUnit_Framework_TestSuite $suite) - { - $this->endedSuites++; - - if($this->startedSuites <= $this->endedSuites) { - $this->notify(); - } - } - - /** - * A test started. - * - * @param PHPUnit_Framework_Test $test - */ - public function startTest(PHPUnit_Framework_Test $test) - { - $this->tests++; - } - - /** - * A test ended. - * - * @param PHPUnit_Framework_Test $test - * @param float $time - */ - public function endTest(PHPUnit_Framework_Test $test, $time) - { - } - - /** - * - */ - protected function notify() - { - $d = new Dbus(Dbus::BUS_SESSION); - - $n = $d->createProxy( - 'org.freedesktop.Notifications', - '/org/freedesktop/Notifications', - 'org.freedesktop.Notifications' - ); - - $n->Notify( - 'PHPUnit_Util_Log_DBUS', - new DBusUInt32(0), - 'phpunit', - 'PHPUnit Test Report', - sprintf( - "Suite: %s\n%d tests run in %s minutes.\n%d errors, %d failures", - $this->suiteName, - $this->tests, - (date('i:s', time() - $this->startTime)), - $this->errors, - $this->failures - ), - new DBusArray(DBus::STRING, array()), - new DBusDict(DBus::VARIANT, array()), - 1000 - ); - } -} diff --git a/libs/PHPUnit/PHPUnit/Util/Log/JSON.php b/libs/PHPUnit/PHPUnit/Util/Log/JSON.php index ba21be5..aa56e56 100644 --- a/libs/PHPUnit/PHPUnit/Util/Log/JSON.php +++ b/libs/PHPUnit/PHPUnit/Util/Log/JSON.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util_Log * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util_Log * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -85,12 +84,9 @@ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) $this->writeCase( 'error', $time, - PHPUnit_Util_Filter::getFilteredStacktrace( - $e, - TRUE, - FALSE - ), - $e->getMessage() + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + $e->getMessage(), + $test ); $this->currentTestPass = FALSE; @@ -108,12 +104,9 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser $this->writeCase( 'fail', $time, - PHPUnit_Util_Filter::getFilteredStacktrace( - $e, - TRUE, - FALSE - ), - $e->getMessage() + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + $e->getMessage(), + $test ); $this->currentTestPass = FALSE; @@ -128,7 +121,13 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser */ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { - $this->writeCase('error', $time, array(), 'Incomplete Test'); + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + 'Incomplete Test: ' . $e->getMessage(), + $test + ); $this->currentTestPass = FALSE; } @@ -142,7 +141,13 @@ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $t */ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { - $this->writeCase('error', $time, array(), 'Skipped Test'); + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE), + 'Skipped Test: ' . $e->getMessage(), + $test + ); $this->currentTestPass = FALSE; } @@ -205,7 +210,7 @@ public function startTest(PHPUnit_Framework_Test $test) public function endTest(PHPUnit_Framework_Test $test, $time) { if ($this->currentTestPass) { - $this->writeCase('pass', $time); + $this->writeCase('pass', $time, array(), '', $test); } } @@ -215,8 +220,12 @@ public function endTest(PHPUnit_Framework_Test $test, $time) * @param array $trace * @param string $message */ - protected function writeCase($status, $time, array $trace = array(), $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', @@ -225,7 +234,8 @@ protected function writeCase($status, $time, array $trace = array(), $message = 'status' => $status, 'time' => $time, 'trace' => $trace, - 'message' => $message + 'message' => PHPUnit_Util_String::convertToUtf8($message), + 'output' => $output, ) ); } @@ -235,6 +245,10 @@ protected function writeCase($status, $time, array $trace = array(), $message = */ public function write($buffer) { - parent::write(json_encode($buffer)); + if (defined('JSON_PRETTY_PRINT')) { + parent::write(json_encode($buffer, JSON_PRETTY_PRINT)); + } else { + parent::write(json_encode($buffer)); + } } } diff --git a/libs/PHPUnit/PHPUnit/Util/Log/JUnit.php b/libs/PHPUnit/PHPUnit/Util/Log/JUnit.php index b56f475..1234101 100644 --- a/libs/PHPUnit/PHPUnit/Util/Log/JUnit.php +++ b/libs/PHPUnit/PHPUnit/Util/Log/JUnit.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util_Log * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -51,9 +51,8 @@ * @package PHPUnit * @subpackage Util_Log * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -174,7 +173,7 @@ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . "\n" . - PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE); + PHPUnit_Util_Filter::getFilteredStacktrace($e); $error = $this->document->createElement( 'error', PHPUnit_Util_XML::prepareString($buffer) @@ -205,11 +204,9 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser $buffer = ''; } - $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e). + $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . "\n" . - PHPUnit_Util_Filter::getFilteredStacktrace( - $e, FALSE - ); + PHPUnit_Util_Filter::getFilteredStacktrace($e); $failure = $this->document->createElement( 'failure', PHPUnit_Util_XML::prepareString($buffer) @@ -238,7 +235,7 @@ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $t 'error', PHPUnit_Util_XML::prepareString( "Incomplete Test\n" . - PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE) + PHPUnit_Util_Filter::getFilteredStacktrace($e) ) ); @@ -267,7 +264,7 @@ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time 'error', PHPUnit_Util_XML::prepareString( "Skipped Test\n" . - PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE) + PHPUnit_Util_Filter::getFilteredStacktrace($e) ) ); diff --git a/libs/PHPUnit/PHPUnit/Util/Log/TAP.php b/libs/PHPUnit/PHPUnit/Util/Log/TAP.php index 1796b26..06d8174 100644 --- a/libs/PHPUnit/PHPUnit/Util/Log/TAP.php +++ b/libs/PHPUnit/PHPUnit/Util/Log/TAP.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,13 +37,15 @@ * @package PHPUnit * @subpackage Util_Log * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 'SymfonyComponents/YAML/sfYamlDumper.php'; +if (!class_exists('Symfony\\Component\\Yaml\\Dumper', FALSE)) { + require_once 'Symfony/Component/Yaml/Dumper.php'; +} /** * A TestListener that generates a logfile of the @@ -52,9 +54,8 @@ * @package PHPUnit * @subpackage Util_Log * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -79,7 +80,7 @@ class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Frame * Constructor. * * @param mixed $out - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.3.4 */ public function __construct($out = NULL) @@ -131,7 +132,7 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser } } - $yaml = new sfYamlDumper(); + $yaml = new Symfony\Component\Yaml\Dumper; $this->write( sprintf( diff --git a/libs/PHPUnit/PHPUnit/Util/Log/XHProf.php b/libs/PHPUnit/PHPUnit/Util/Log/XHProf.php deleted file mode 100644 index 0e6084b..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Log/XHProf.php +++ /dev/null @@ -1,252 +0,0 @@ -. - * 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 Benjamin Eberlei - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.5.0 - */ - -/** - * A TestListener that integrates with XHProf. - * - * Here is an example XML configuration for activating this listener: - * - * - * - * - * - * - * - * /var/www/xhprof_lib/utils/xhprof_lib.php - * - * - * /var/www/xhprof_lib/utils/xhprof_runs.php - * - * - * http://localhost/xhprof_html/index.php - * - * - * Doctrine2 - * - * - * XHPROF_FLAGS_CPU,XHPROF_FLAGS_MEMORY - * - * - * - * - * - * - * - * @package PHPUnit - * @subpackage Util_Log - * @author Benjamin Eberlei - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.5.0 - */ -class PHPUnit_Util_Log_XHProf implements PHPUnit_Framework_TestListener -{ - /** - * @var array - */ - protected $runs = array(); - - /** - * @var array - */ - protected $options = array(); - - /** - * @var integer - */ - protected $suites = 0; - - /** - * Constructor. - * - * @param array $options - */ - public function __construct(array $options = array()) - { - if (!extension_loaded('xhprof')) { - throw new RuntimeException( - 'The XHProf extension is required for this listener to work.' - ); - } - - if (!isset($options['appNamespace'])) { - throw new InvalidArgumentException( - 'The "appNamespace" option is not set.' - ); - } - - if (!isset($options['xhprofLibFile']) || - !file_exists($options['xhprofLibFile'])) { - throw new InvalidArgumentException( - 'The "xhprofLibFile" option is not set or the configured file does not exist' - ); - } - - if (!isset($options['xhprofRunsFile']) || - !file_exists($options['xhprofRunsFile'])) { - throw new InvalidArgumentException( - 'The "xhprofRunsFile" option is not set or the configured file does not exist' - ); - } - - require_once $options['xhprofLibFile']; - require_once $options['xhprofRunsFile']; - - $this->options = $options; - } - - /** - * 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 - */ - public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) - { - } - - /** - * A test started. - * - * @param PHPUnit_Framework_Test $test - */ - public function startTest(PHPUnit_Framework_Test $test) - { - if (!isset($this->options['xhprofFlags'])) { - $flags = XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY; - } else { - $flags = 0; - - foreach (explode(',', $this->options['xhprofFlags']) as $flag) { - $flags += constant($flag); - } - } - - xhprof_enable($flags); - } - - /** - * A test ended. - * - * @param PHPUnit_Framework_Test $test - * @param float $time - */ - public function endTest(PHPUnit_Framework_Test $test, $time) - { - $data = xhprof_disable(); - $runs = new XHProfRuns_Default; - $run = $runs->save_run($data, $this->options['appNamespace']); - $this->runs[] = $this->options['xhprofWeb'] . '?run=' . $run . - '&source=' . $this->options['appNamespace']; - } - - /** - * A test suite started. - * - * @param PHPUnit_Framework_TestSuite $suite - */ - public function startTestSuite(PHPUnit_Framework_TestSuite $suite) - { - $this->suites++; - } - - /** - * A test suite ended. - * - * @param PHPUnit_Framework_TestSuite $suite - */ - public function endTestSuite(PHPUnit_Framework_TestSuite $suite) - { - $this->suites--; - - if ($this->suites == 0) { - print "\n\nXHProf runs: " . count($this->runs) . "\n"; - - foreach ($this->runs as $run) { - print ' * ' . $run . "\n"; - } - - print "\n"; - } - } -} diff --git a/libs/PHPUnit/PHPUnit/Util/PHP.php b/libs/PHPUnit/PHPUnit/Util/PHP.php index ecf7c03..7c75cf1 100644 --- a/libs/PHPUnit/PHPUnit/Util/PHP.php +++ b/libs/PHPUnit/PHPUnit/Util/PHP.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,20 +49,17 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 { /** - * Path to the PHP interpreter that is to be used. - * - * @var string $phpBinary + * @var string $phpBinary */ - protected static $phpBinary = NULL; + protected $phpBinary; /** * Returns the path to a PHP interpreter. @@ -72,48 +69,69 @@ abstract class PHPUnit_Util_PHP * * When not set, the following assumptions will be made: * - * 1. When the PHP CLI/CGI binary configured with the PEAR Installer - * (php_bin configuration value) is readable, it will be used. - * - * 2. When PHPUnit is run using the CLI SAPI and the $_SERVER['_'] + * 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. * - * 3. When PHPUnit is run using the CLI SAPI and the $_SERVER['_'] + * 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 */ - public static function getPhpBinary() + protected function getPhpBinary() { - if (self::$phpBinary === NULL) { - if (is_readable('@php_bin@')) { - self::$phpBinary = '@php_bin@'; + 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['_']; + } } - else if (PHP_SAPI == 'cli' && isset($_SERVER['_']) && - strpos($_SERVER['_'], 'phpunit') !== FALSE) { - $file = file($_SERVER['_']); - $tmp = explode(' ', $file[0]); - self::$phpBinary = trim($tmp[1]); + 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(self::$phpBinary)) { - self::$phpBinary = 'php'; + if (!is_readable($this->phpBinary)) { + $this->phpBinary = 'php'; } else { - self::$phpBinary = escapeshellarg(self::$phpBinary); + $this->phpBinary = escapeshellcmd($this->phpBinary); } } - return self::$phpBinary; + return $this->phpBinary; } /** @@ -141,7 +159,7 @@ public static function factory() public function runJob($job, PHPUnit_Framework_Test $test = NULL, PHPUnit_Framework_TestResult $result = NULL) { $process = proc_open( - self::getPhpBinary(), + $this->getPhpBinary(), array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), @@ -208,10 +226,24 @@ protected function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Fram $time = 0; $result->addError( $test, - new RuntimeException(trim($stderr)), $time + new PHPUnit_Framework_Exception(trim($stderr)), $time ); } else { - $childResult = @unserialize($stdout); + 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'])) { @@ -224,14 +256,9 @@ protected function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Fram $childResult = $childResult['result']; if ($result->getCollectCodeCoverageInformation()) { - $codeCoverageInformation = $childResult->getRawCodeCoverageInformation(); - - if (isset($codeCoverageInformation[0]) && - is_array($codeCoverageInformation[0])) { - $result->getCodeCoverage()->append( - $codeCoverageInformation[0], $test - ); - } + $result->getCodeCoverage()->merge( + $childResult->getCodeCoverage() + ); } $time = $childResult->time(); @@ -242,36 +269,64 @@ protected function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Fram if (!empty($notImplemented)) { $result->addError( - $test, $notImplemented[0]->thrownException(), $time + $test, $this->getException($notImplemented[0]), $time ); } else if (!empty($skipped)) { $result->addError( - $test, $skipped[0]->thrownException(), $time + $test, $this->getException($skipped[0]), $time ); } else if (!empty($errors)) { $result->addError( - $test, $errors[0]->thrownException(), $time + $test, $this->getException($errors[0]), $time ); } else if (!empty($failures)) { $result->addFailure( - $test, $failures[0]->thrownException(), $time + $test, $this->getException($failures[0]), $time ); } - } else { - $time = 0; - - $result->addError( - $test, new RuntimeException(trim($stdout)), $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/libs/PHPUnit/PHPUnit/Util/PHP/Default.php b/libs/PHPUnit/PHPUnit/Util/PHP/Default.php index f0d99a7..f6b08aa 100644 --- a/libs/PHPUnit/PHPUnit/Util/PHP/Default.php +++ b/libs/PHPUnit/PHPUnit/Util/PHP/Default.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/PHP/Windows.php b/libs/PHPUnit/PHPUnit/Util/PHP/Windows.php index 3a3e0cf..d682b89 100644 --- a/libs/PHPUnit/PHPUnit/Util/PHP/Windows.php +++ b/libs/PHPUnit/PHPUnit/Util/PHP/Windows.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -77,7 +76,7 @@ protected function process($pipe, $job) fwrite( $pipe, - "tempFile, "'") . "'; ?>" + "tempFile, TRUE) . "; ?>" ); } diff --git a/libs/PHPUnit/PHPUnit/Util/Printer.php b/libs/PHPUnit/PHPUnit/Util/Printer.php index da0567c..e59c2ce 100644 --- a/libs/PHPUnit/PHPUnit/Util/Printer.php +++ b/libs/PHPUnit/PHPUnit/Util/Printer.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,13 +49,12 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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_Util_Printer +class PHPUnit_Util_Printer { /** * If TRUE, flush output after every write. @@ -83,7 +82,7 @@ abstract class PHPUnit_Util_Printer * Constructor. * * @param mixed $out - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception */ public function __construct($out = NULL) { @@ -93,14 +92,14 @@ public function __construct($out = NULL) $out = explode(':', str_replace('socket://', '', $out)); if (sizeof($out) != 2) { - throw new InvalidArgumentException; + 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); + mkdir(dirname($out), 0777, TRUE); } $this->out = fopen($out, 'wt'); @@ -114,11 +113,11 @@ public function __construct($out = NULL) } /** - * Flush buffer, optionally tidy up HTML, and close output. + * Flush buffer, optionally tidy up HTML, and close output if it's not to a php stream */ public function flush() { - if ($this->out && $this->outTarget !== 'php://stderr') { + if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) { fclose($this->out); } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton.php b/libs/PHPUnit/PHPUnit/Util/Skeleton.php deleted file mode 100644 index ed9e848..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton.php +++ /dev/null @@ -1,146 +0,0 @@ -. - * 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 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 2.1.0 - */ - -/** - * Generator for skeletons. - * - * @package PHPUnit - * @subpackage Util - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 2.1.0 - */ -abstract class PHPUnit_Util_Skeleton -{ - /** - * @var array - */ - protected $inClassName; - - /** - * @var string - */ - protected $inSourceFile; - - /** - * @var array - */ - protected $outClassName; - - /** - * @var string - */ - protected $outSourceFile; - - /** - * Constructor. - * - * @param string $inClassName - * @param string $inSourceFile - * @param string $outClassName - * @param string $outSourceFile - * @since Method available since Release 3.4.0 - */ - public function __construct($inClassName, $inSourceFile = '', $outClassName = '', $outSourceFile = '') - { - $this->inClassName = PHPUnit_Util_Class::parseFullyQualifiedClassName( - $inClassName - ); - - $this->outClassName = PHPUnit_Util_Class::parseFullyQualifiedClassName( - $outClassName - ); - - $this->inSourceFile = str_replace( - $this->inClassName['fullyQualifiedClassName'], - $this->inClassName['className'], - $inSourceFile - ); - - $this->outSourceFile = str_replace( - $this->outClassName['fullyQualifiedClassName'], - $this->outClassName['className'], - $outSourceFile - ); - } - - /** - * @return string - */ - public function getOutClassName() - { - return $this->outClassName['fullyQualifiedClassName']; - } - - /** - * @return string - */ - public function getOutSourceFile() - { - return $this->outSourceFile; - } - - /** - * Generates the code and writes it to a source file. - * - * @param string $file - */ - public function write($file = '') - { - if ($file == '') { - $file = $this->outSourceFile; - } - - if ($fp = @fopen($file, 'wt')) { - @fwrite($fp, $this->generate()); - @fclose($fp); - } - } - - abstract public function generate(); -} diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Class.php b/libs/PHPUnit/PHPUnit/Util/Skeleton/Class.php deleted file mode 100644 index 1360e22..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Class.php +++ /dev/null @@ -1,327 +0,0 @@ -. - * 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_Skeleton - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.0 - */ - -require_once 'Text/Template.php'; - -/** - * Generator for class skeletons from test classes. - * - * @package PHPUnit - * @subpackage Util_Skeleton - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.0 - */ -class PHPUnit_Util_Skeleton_Class extends PHPUnit_Util_Skeleton -{ - /** - * Constructor. - * - * @param string $inClassName - * @param string $inSourceFile - * @param string $outClassName - * @param string $outSourceFile - * @throws RuntimeException - */ - public function __construct($inClassName, $inSourceFile = '', $outClassName = '', $outSourceFile = '') - { - if (empty($inSourceFile)) { - $inSourceFile = $inClassName . '.php'; - } - - if (!is_file($inSourceFile)) { - throw new PHPUnit_Framework_Exception( - sprintf( - '"%s" could not be opened.', - - $inSourceFile - ) - ); - } - - if (empty($outClassName)) { - $outClassName = substr($inClassName, 0, strlen($inClassName) - 4); - } - - if (empty($outSourceFile)) { - $outSourceFile = dirname($inSourceFile) . DIRECTORY_SEPARATOR . - $outClassName . '.php'; - } - - parent::__construct( - $inClassName, $inSourceFile, $outClassName, $outSourceFile - ); - } - - /** - * Generates the class' source. - * - * @return mixed - */ - public function generate() - { - $methods = ''; - - foreach ($this->findTestedMethods() as $method) { - $methodTemplate = new Text_Template( - sprintf( - '%s%sTemplate%sMethod.tpl', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR - ) - ); - - $methodTemplate->setVar( - array( - 'methodName' => $method, - ) - ); - - $methods .= $methodTemplate->render(); - } - - $classTemplate = new Text_Template( - sprintf( - '%s%sTemplate%sClass.tpl', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR - ) - ); - - $classTemplate->setVar( - array( - 'className' => $this->outClassName['fullyQualifiedClassName'], - 'methods' => $methods, - 'date' => date('Y-m-d'), - 'time' => date('H:i:s') - ) - ); - - return $classTemplate->render(); - } - - /** - * Returns the methods of the class under test - * that are called from the test methods. - * - * @return array - */ - protected function findTestedMethods() - { - $setUpVariables = array(); - $testedMethods = array(); - $classes = PHPUnit_Util_File::getClassesInFile( - $this->inSourceFile - ); - $testMethods = $classes[$this->inClassName['fullyQualifiedClassName']]['methods']; - unset($classes); - - foreach ($testMethods as $name => $testMethod) { - if (strtolower($name) == 'setup') { - $setUpVariables = $this->findVariablesThatReferenceClass( - $testMethod['tokens'] - ); - - break; - } - } - - foreach ($testMethods as $name => $testMethod) { - $argVariables = array(); - - if (strtolower($name) == 'setup') { - continue; - } - - $start = strpos($testMethod['signature'], '(') + 1; - $end = strlen($testMethod['signature']) - $start - 1; - $args = substr($testMethod['signature'], $start, $end); - - foreach (explode(',', $args) as $arg) { - $arg = explode(' ', trim($arg)); - - if (count($arg) == 2) { - $type = $arg[0]; - $var = $arg[1]; - } else { - $type = NULL; - $var = $arg[0]; - } - - if ($type == $this->outClassName['fullyQualifiedClassName']) { - $argVariables[] = $var; - } - } - - $variables = array_unique( - array_merge( - $setUpVariables, - $argVariables, - $this->findVariablesThatReferenceClass($testMethod['tokens']) - ) - ); - - foreach ($testMethod['tokens'] as $i => $token) { - // Class::method() - if (is_array($token) && $token[0] == T_DOUBLE_COLON && - is_array($testMethod['tokens'][$i-1]) && - $testMethod['tokens'][$i-1][0] == T_STRING && - $testMethod['tokens'][$i-1][1] == $this->outClassName['fullyQualifiedClassName'] && - is_array($testMethod['tokens'][$i+1]) && - $testMethod['tokens'][$i+1][0] == T_STRING && - $testMethod['tokens'][$i+2] == '(') { - $testedMethods[] = $testMethod['tokens'][$i+1][1]; - } - - // $this->object->method() - else if (is_array($token) && $token[0] == T_OBJECT_OPERATOR && - in_array($this->findVariableName($testMethod['tokens'], $i), $variables) && - is_array($testMethod['tokens'][$i+2]) && - $testMethod['tokens'][$i+2][0] == T_OBJECT_OPERATOR && - is_array($testMethod['tokens'][$i+3]) && - $testMethod['tokens'][$i+3][0] == T_STRING && - $testMethod['tokens'][$i+4] == '(') { - $testedMethods[] = $testMethod['tokens'][$i+3][1]; - } - - // $object->method() - else if (is_array($token) && $token[0] == T_OBJECT_OPERATOR && - in_array($this->findVariableName($testMethod['tokens'], $i), $variables) && - is_array($testMethod['tokens'][$i+1]) && - $testMethod['tokens'][$i+1][0] == T_STRING && - $testMethod['tokens'][$i+2] == '(') { - $testedMethods[] = $testMethod['tokens'][$i+1][1]; - } - } - } - - $testedMethods = array_unique($testedMethods); - sort($testedMethods); - - return $testedMethods; - } - - /** - * Returns the variables used in test methods - * that reference the class under test. - * - * @param array $tokens - * @return array - */ - protected function findVariablesThatReferenceClass(array $tokens) - { - $inNew = FALSE; - $variables = array(); - - foreach ($tokens as $i => $token) { - if (is_string($token)) { - if (trim($token) == ';') { - $inNew = FALSE; - } - - continue; - } - - list ($token, $value) = $token; - - switch ($token) { - case T_NEW: { - $inNew = TRUE; - } - break; - - case T_STRING: { - if ($inNew) { - if ($value == $this->outClassName['fullyQualifiedClassName']) { - $variables[] = $this->findVariableName( - $tokens, $i - ); - } - } - - $inNew = FALSE; - } - break; - } - } - - return $variables; - } - - /** - * Finds the variable name of the object for the method call - * that is currently being processed. - * - * @param array $tokens - * @param integer $start - * @return mixed - */ - protected function findVariableName(array $tokens, $start) - { - for ($i = $start - 1; $i >= 0; $i--) { - if (is_array($tokens[$i]) && $tokens[$i][0] == T_VARIABLE) { - $variable = $tokens[$i][1]; - - if (is_array($tokens[$i+1]) && - $tokens[$i+1][0] == T_OBJECT_OPERATOR && - $tokens[$i+2] != '(' && - $tokens[$i+3] != '(') { - $variable .= '->' . $tokens[$i+2][1]; - } - - return $variable; - } - } - - return FALSE; - } -} diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Class.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Class.tpl.dist deleted file mode 100644 index 2a29e5e..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Class.tpl.dist +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/IncompleteTestMethod.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/IncompleteTestMethod.tpl.dist deleted file mode 100644 index d89f90e..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/IncompleteTestMethod.tpl.dist +++ /dev/null @@ -1,11 +0,0 @@ - - /** - * @todo Implement test{methodName}(). - */ - public function test{methodName}() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Method.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Method.tpl.dist deleted file mode 100644 index dd3b044..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/Method.tpl.dist +++ /dev/null @@ -1,9 +0,0 @@ - - /** - * @todo Implement {methodName}(). - */ - public function {methodName}() - { - // Remove the following line when you implement this method. - throw new RuntimeException('Not yet implemented.'); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestClass.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestClass.tpl.dist deleted file mode 100644 index 68e10e3..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestClass.tpl.dist +++ /dev/null @@ -1,31 +0,0 @@ -object = new {className}; - } - - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } -{methods}} -?> diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethod.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethod.tpl.dist deleted file mode 100644 index 68acf2b..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethod.tpl.dist +++ /dev/null @@ -1,11 +0,0 @@ - - /** - * Generated from @assert {annotation}. - */ - public function test{methodName}() - { - $this->assert{assertion}( - {expected}, - $this->object->{origMethodName}({arguments}) - ); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBool.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBool.tpl.dist deleted file mode 100644 index 483a272..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBool.tpl.dist +++ /dev/null @@ -1,10 +0,0 @@ - - /** - * Generated from @assert {annotation}. - */ - public function test{methodName}() - { - $this->assert{assertion}( - $this->object->{origMethodName}({arguments}) - ); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBoolStatic.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBoolStatic.tpl.dist deleted file mode 100644 index f46d84d..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodBoolStatic.tpl.dist +++ /dev/null @@ -1,10 +0,0 @@ - - /** - * Generated from @assert {annotation}. - */ - public function test{methodName}() - { - $this->assert{assertion}( - {className}::{origMethodName}({arguments}) - ); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodException.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodException.tpl.dist deleted file mode 100644 index 75c02b0..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodException.tpl.dist +++ /dev/null @@ -1,9 +0,0 @@ - - /** - * Generated from @assert {annotation}. - * @expectedException {expected} - */ - public function test{methodName}() - { - $this->object->{origMethodName}({arguments}); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodExceptionStatic.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodExceptionStatic.tpl.dist deleted file mode 100644 index 5f27729..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodExceptionStatic.tpl.dist +++ /dev/null @@ -1,9 +0,0 @@ - - /** - * Generated from @assert {annotation}. - * @expectedException {expected} - */ - public function test{methodName}() - { - {className}::{origMethodName}({arguments}); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodStatic.tpl.dist b/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodStatic.tpl.dist deleted file mode 100644 index b4fe571..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Template/TestMethodStatic.tpl.dist +++ /dev/null @@ -1,11 +0,0 @@ - - /** - * Generated from @assert {annotation}. - */ - public function test{methodName}() - { - $this->assert{assertion}( - {expected}, - {className}::{origMethodName}({arguments}) - ); - } diff --git a/libs/PHPUnit/PHPUnit/Util/Skeleton/Test.php b/libs/PHPUnit/PHPUnit/Util/Skeleton/Test.php deleted file mode 100644 index cc221ca..0000000 --- a/libs/PHPUnit/PHPUnit/Util/Skeleton/Test.php +++ /dev/null @@ -1,379 +0,0 @@ -. - * 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_Skeleton - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.phpunit.de/ - * @since File available since Release 3.3.0 - */ - -require_once 'Text/Template.php'; - -/** - * Generator for test class skeletons from classes. - * - * @package PHPUnit - * @subpackage Util_Skeleton - * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 - * @link http://www.phpunit.de/ - * @since Class available since Release 3.3.0 - */ -class PHPUnit_Util_Skeleton_Test extends PHPUnit_Util_Skeleton -{ - /** - * @var array - */ - protected $methodNameCounter = array(); - - /** - * Constructor. - * - * @param string $inClassName - * @param string $inSourceFile - * @param string $outClassName - * @param string $outSourceFile - * @throws RuntimeException - */ - public function __construct($inClassName, $inSourceFile = '', $outClassName = '', $outSourceFile = '') - { - if (class_exists($inClassName)) { - $reflector = new ReflectionClass($inClassName); - $inSourceFile = $reflector->getFileName(); - - if ($inSourceFile === FALSE) { - $inSourceFile = ''; - } - - unset($reflector); - } else { - if (empty($inSourceFile)) { - $possibleFilenames = array( - $inClassName . '.php', - PHPUnit_Util_Filesystem::classNameToFilename($inClassName) - ); - - foreach ($possibleFilenames as $possibleFilename) { - if (is_file($possibleFilename)) { - $inSourceFile = $possibleFilename; - } - } - } - - if (empty($inSourceFile)) { - throw new PHPUnit_Framework_Exception( - sprintf( - 'Neither "%s" nor "%s" could be opened.', - $possibleFilenames[0], - $possibleFilenames[1] - ) - ); - } - - if (!is_file($inSourceFile)) { - throw new PHPUnit_Framework_Exception( - sprintf( - '"%s" could not be opened.', - - $inSourceFile - ) - ); - } - - $inSourceFile = realpath($inSourceFile); - include_once $inSourceFile; - - if (!class_exists($inClassName)) { - throw new PHPUnit_Framework_Exception( - sprintf( - 'Could not find class "%s" in "%s".', - - $inClassName, - $inSourceFile - ) - ); - } - } - - if (empty($outClassName)) { - $outClassName = $inClassName . 'Test'; - } - - if (empty($outSourceFile)) { - $outSourceFile = dirname($inSourceFile) . DIRECTORY_SEPARATOR . $outClassName . '.php'; - } - - parent::__construct( - $inClassName, $inSourceFile, $outClassName, $outSourceFile - ); - } - - /** - * Generates the test class' source. - * - * @param boolean $verbose - * @return mixed - */ - public function generate($verbose = FALSE) - { - $class = new ReflectionClass( - $this->inClassName['fullyQualifiedClassName'] - ); - $methods = ''; - $incompleteMethods = ''; - - foreach ($class->getMethods() as $method) { - if (!$method->isConstructor() && - !$method->isAbstract() && - $method->isPublic() && - $method->getDeclaringClass()->getName() == $this->inClassName['fullyQualifiedClassName']) { - $assertAnnotationFound = FALSE; - - if (preg_match_all('/@assert(.*)$/Um', $method->getDocComment(), $annotations)) { - foreach ($annotations[1] as $annotation) { - if (preg_match('/\((.*)\)\s+([^\s]*)\s+(.*)/', $annotation, $matches)) { - switch ($matches[2]) { - case '==': { - $assertion = 'Equals'; - } - break; - - case '!=': { - $assertion = 'NotEquals'; - } - break; - - case '===': { - $assertion = 'Same'; - } - break; - - case '!==': { - $assertion = 'NotSame'; - } - break; - - case '>': { - $assertion = 'GreaterThan'; - } - break; - - case '>=': { - $assertion = 'GreaterThanOrEqual'; - } - break; - - case '<': { - $assertion = 'LessThan'; - } - break; - - case '<=': { - $assertion = 'LessThanOrEqual'; - } - break; - - case 'throws': { - $assertion = 'exception'; - } - break; - - default: { - throw new PHPUnit_Framework_Exception( - sprintf( - 'Token "%s" could not be parsed in @assert annotation.', - $matches[2] - ) - ); - } - } - - if ($assertion == 'exception') { - $template = 'TestMethodException'; - } - - else if ($assertion == 'Equals' && - strtolower($matches[3]) == 'true') { - $assertion = 'True'; - $template = 'TestMethodBool'; - } - - else if ($assertion == 'NotEquals' && - strtolower($matches[3]) == 'true') { - $assertion = 'False'; - $template = 'TestMethodBool'; - } - - else if ($assertion == 'Equals' && - strtolower($matches[3]) == 'false') { - $assertion = 'False'; - $template = 'TestMethodBool'; - } - - else if ($assertion == 'NotEquals' && - strtolower($matches[3]) == 'false') { - $assertion = 'True'; - $template = 'TestMethodBool'; - } - - else { - $template = 'TestMethod'; - } - - if ($method->isStatic()) { - $template .= 'Static'; - } - - $methodTemplate = new Text_Template( - sprintf( - '%s%sTemplate%s%s.tpl', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR, - $template - ) - ); - - $origMethodName = $method->getName(); - $methodName = ucfirst($origMethodName); - - if (isset($this->methodNameCounter[$methodName])) { - $this->methodNameCounter[$methodName]++; - } else { - $this->methodNameCounter[$methodName] = 1; - } - - if ($this->methodNameCounter[$methodName] > 1) { - $methodName .= $this->methodNameCounter[$methodName]; - } - - $methodTemplate->setVar( - array( - 'annotation' => trim($annotation), - 'arguments' => $matches[1], - 'assertion' => isset($assertion) ? $assertion : '', - 'expected' => $matches[3], - 'origMethodName' => $origMethodName, - 'className' => $this->inClassName['fullyQualifiedClassName'], - 'methodName' => $methodName - ) - ); - - $methods .= $methodTemplate->render(); - - $assertAnnotationFound = TRUE; - } - } - } - - if (!$assertAnnotationFound) { - $methodTemplate = new Text_Template( - sprintf( - '%s%sTemplate%sIncompleteTestMethod.tpl', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR - ) - ); - - $methodTemplate->setVar( - array( - 'methodName' => ucfirst($method->getName()) - ) - ); - - $incompleteMethods .= $methodTemplate->render(); - } - } - } - - $classTemplate = new Text_Template( - sprintf( - '%s%sTemplate%sTestClass.tpl', - - dirname(__FILE__), - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR - ) - ); - - if ($this->inSourceFile != '') { - $requireClassFile = sprintf( - "\n\nrequire_once '%s';", - - $this->inSourceFile - ); - } else { - $requireClassFile = ''; - } - - if ($this->outClassName['namespace'] != '') { - $namespace = "\nnamespace " . - $this->outClassName['namespace'] . ";\n"; - } else { - $namespace = ''; - } - - $classTemplate->setVar( - array( - 'namespace' => $namespace, - 'namespaceSeparator' => !empty($namespace) ? '\\' : '', - 'className' => $this->inClassName['className'], - 'testClassName' => $this->outClassName['className'], - 'requireClassFile' => $requireClassFile, - 'methods' => $methods . $incompleteMethods, - 'date' => date('Y-m-d'), - 'time' => date('H:i:s') - ) - ); - - if (!$verbose) { - return $classTemplate->render(); - } else { - return array( - 'code' => $classTemplate->render(), - 'incomplete' => empty($methods) - ); - } - } -} diff --git a/libs/PHPUnit/PHPUnit/Util/String.php b/libs/PHPUnit/PHPUnit/Util/String.php new file mode 100644 index 0000000..c442fdd --- /dev/null +++ b/libs/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/libs/PHPUnit/PHPUnit/Util/Test.php b/libs/PHPUnit/PHPUnit/Util/Test.php index 52fae1f..ea11d2d 100644 --- a/libs/PHPUnit/PHPUnit/Util/Test.php +++ b/libs/PHPUnit/PHPUnit/Util/Test.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -59,6 +58,12 @@ 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(); @@ -96,6 +101,40 @@ public static function describe(PHPUnit_Framework_Test $test, $asString = TRUE) } } + /** + * 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. * @@ -115,7 +154,7 @@ public static function getExpectedException($className, $methodName) ); $class = $matches[1]; - $code = 0; + $code = NULL; $message = ''; if (isset($matches[2])) { @@ -123,15 +162,27 @@ public static function getExpectedException($className, $methodName) } else if (isset($annotations['method']['expectedExceptionMessage'])) { - $message = $annotations['method']['expectedExceptionMessage'][0]; + $message = self::_parseAnnotationContent( + $annotations['method']['expectedExceptionMessage'][0] + ); } if (isset($matches[3])) { - $code = (int)$matches[3]; + $code = $matches[3]; } else if (isset($annotations['method']['expectedExceptionCode'])) { - $code = (int)$annotations['method']['expectedExceptionCode'][0]; + $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( @@ -142,6 +193,26 @@ public static function getExpectedException($className, $methodName) 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. * @@ -197,7 +268,7 @@ public static function getProvidedData($className, $methodName) if ($data !== NULL) { foreach ($data as $key => $value) { if (!is_array($value)) { - throw new InvalidArgumentException( + throw new PHPUnit_Framework_Exception( sprintf( 'Data set %s is invalid.', is_int($key) ? '#' . $key : '"' . $key . '"' @@ -225,8 +296,13 @@ public static function parseTestMethodAnnotations($className, $methodName = '') } if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) { - $method = new ReflectionMethod($className, $methodName); - self::$annotationCache[$className . '::' . $methodName] = self::parseAnnotations($method->getDocComment()); + try { + $method = new ReflectionMethod($className, $methodName); + $annotations = self::parseAnnotations($method->getDocComment()); + } catch (ReflectionException $e) { + $annotations = array(); + } + self::$annotationCache[$className . '::' . $methodName] = $annotations; } return array( @@ -243,6 +319,8 @@ public static function parseTestMethodAnnotations($className, $methodName = '') 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]); @@ -351,9 +429,59 @@ public static function getGroups($className, $methodName = '') $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. * diff --git a/libs/PHPUnit/PHPUnit/Util/TestDox/NamePrettifier.php b/libs/PHPUnit/PHPUnit/Util/TestDox/NamePrettifier.php index 1d85659..37ae2b4 100644 --- a/libs/PHPUnit/PHPUnit/Util/TestDox/NamePrettifier.php +++ b/libs/PHPUnit/PHPUnit/Util/TestDox/NamePrettifier.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter.php b/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter.php index 85d25d5..6a47c22 100644 --- a/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter.php +++ b/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php b/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php index a9bd606..5c9d6d0 100644 --- a/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php +++ b/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/Text.php b/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/Text.php index 65958a3..88868dd 100644 --- a/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/Text.php +++ b/libs/PHPUnit/PHPUnit/Util/TestDox/ResultPrinter/Text.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util_TestDox * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/TestSuiteIterator.php b/libs/PHPUnit/PHPUnit/Util/TestSuiteIterator.php index 25bcbab..1407f7f 100644 --- a/libs/PHPUnit/PHPUnit/Util/TestSuiteIterator.php +++ b/libs/PHPUnit/PHPUnit/Util/TestSuiteIterator.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ diff --git a/libs/PHPUnit/PHPUnit/Util/Type.php b/libs/PHPUnit/PHPUnit/Util/Type.php index ca43122..9da081f 100644 --- a/libs/PHPUnit/PHPUnit/Util/Type.php +++ b/libs/PHPUnit/PHPUnit/Util/Type.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -78,115 +77,227 @@ public static function isType($type) ); } - public static function shortenedExport($value) + /** + * 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) { - if (is_string($value)) { - return self::shortenedString($value); + 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'; } - elseif (is_array($value)) { - if (count($value) == 0) { - return 'array()'; - } + if ($value === TRUE) { + return 'true'; + } - $a1 = array_slice($value, 0, 1, TRUE); - $k1 = key($a1); - $v1 = $a1[$k1]; + if ($value === FALSE) { + return 'false'; + } - if (is_string($v1)) { - $v1 = self::shortenedString($v1); + 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); } - elseif (is_array($v1)) { - $v1 = 'array(...)'; - } else { - $v1 = self::toString($v1); + 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) + ); } - $a2 = FALSE; + $processedObjects[] = $value; + + // Convert object to array + $value = self::toArray($value); + } - if (count($value) > 1) { - $a2 = array_slice($value, -1, 1, TRUE); - $k2 = key($a2); - $v2 = $a2[$k2]; + if (is_array($value)) { + $whitespace = str_repeat(' ', $indentation); - if (is_string($v2)) { - $v2 = self::shortenedString($v2); - } + // 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]); - elseif (is_array($v2)) { - $v2 = 'array(...)'; - } else { - $v2 = self::toString($v2); + // 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; } } - $text = 'array( ' . self::toString($k1) . ' => ' . $v1; + $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 ($a2 !== FALSE) { - $text .= ', ..., ' . self::toString($k2) . ' => ' . $v2 . ' )'; - } else { - $text .= ' )'; + if (strlen($content) > 0) { + $content = "\n" . $content . $whitespace; } - return $text; + return sprintf( + "%s (%s)", + + is_object($origValue) ? get_class($origValue) . ' Object' : 'Array', + $content + ); } - elseif (is_object($value)) { - return get_class($value) . '(...)'; + if (is_double($value) && (double)(integer)$value === $value) { + return $value . '.0'; } - return self::toString($value); + return (string)$value; } - public static function shortenedString($string) + /** + * 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) { - $string = preg_replace('#\n|\r\n|\r#', ' ', $string); + if (is_string($value)) { + return self::shortenedString($value); + } - if (strlen($string) > 14) { - return PHPUnit_Util_Type::toString( - substr($string, 0, 7) . '...' . substr($string, -7) + $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 ? '...' : '' ); - } else { - return PHPUnit_Util_Type::toString($string); } + + return self::export($value); } - public static function toString($value, $short = FALSE) + /** + * 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) { - if (is_array($value) || is_object($value)) { - if (!$short) { - return "\n" . print_r($value, TRUE); - } else { - if (is_array($value)) { - return ''; - } else { - return '<' . get_class($value) . '>'; - } - } - } + $string = self::export($string); - if (is_string($value) && strpos($value, "\n") !== FALSE) { - return ''; + if (strlen($string) > $maxLength) { + $string = substr($string, 0, $maxLength - 10) . '...' . substr($string, -7); } - if (!is_null($value)) { - $type = gettype($value) . ':'; - } else { - $type = ''; - $value = 'null'; - } + return str_replace("\n", '\n', $string); + } - if (is_bool($value)) { - if ($value === TRUE) { - $value = 'true'; + /** + * 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]; } - else if ($value === FALSE) { - $value = 'false'; + $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 '<' . $type . $value . '>'; + return $array; } } diff --git a/libs/PHPUnit/PHPUnit/Util/XML.php b/libs/PHPUnit/PHPUnit/Util/XML.php index b8d48f9..933c493 100644 --- a/libs/PHPUnit/PHPUnit/Util/XML.php +++ b/libs/PHPUnit/PHPUnit/Util/XML.php @@ -2,7 +2,7 @@ /** * PHPUnit * - * Copyright (c) 2002-2011, Sebastian Bergmann . + * Copyright (c) 2001-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @copyright 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 */ @@ -49,9 +49,8 @@ * @package PHPUnit * @subpackage Util * @author Sebastian Bergmann - * @copyright 2002-2011 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 3.5.14 + * @copyright 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 */ @@ -69,82 +68,21 @@ public static function prepareString($string) '([\\x00-\\x04\\x0b\\x0c\\x0e-\\x1f\\x7f])e', 'sprintf( "&#x%02x;", ord( "\\1" ) )', htmlspecialchars( - self::convertToUtf8($string), ENT_COMPAT, 'UTF-8' + PHPUnit_Util_String::convertToUtf8($string), ENT_COMPAT, 'UTF-8' ) ); } - /** - * Converts a string to UTF-8 encoding. - * - * @param string $string - * @return string - * @since Method available since Release 3.2.19 - */ - protected 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 - * @since Method available since Release 3.3.0 - */ - 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; - } - /** * 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) + public static function loadFile($filename, $isHtml = FALSE, $xinclude = FALSE) { $reporting = error_reporting(0); $contents = file_get_contents($filename); @@ -159,7 +97,7 @@ public static function loadFile($filename, $isHtml = FALSE) ); } - return self::load($contents, $isHtml, $filename); + return self::load($contents, $isHtml, $filename, $xinclude); } /** @@ -168,7 +106,9 @@ public static function loadFile($filename, $isHtml = FALSE) * * 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. + * 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 @@ -177,18 +117,21 @@ public static function loadFile($filename, $isHtml = FALSE) * @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 = '') + 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); @@ -199,6 +142,15 @@ public static function load($actual, $isHtml = FALSE, $filename = '') $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; } @@ -336,7 +288,7 @@ public static function xmlToVariable(DOMElement $element) * @param array $hash * @param array $validKeys * @return array - * @throws InvalidArgumentException + * @throws PHPUnit_Framework_Exception * @since Method available since Release 3.3.0 * @author Mike Naberezny * @author Derek DeVries @@ -361,7 +313,7 @@ public static function assertValidKeys(array $hash, array $validKeys) } if (!empty($unknown)) { - throw new InvalidArgumentException( + throw new PHPUnit_Framework_Exception( 'Unknown key(s): ' . implode(', ', $unknown) ); } @@ -684,6 +636,13 @@ public static function findNodes(DOMDocument $dom, array $options, $isHtml = TRU } } + // 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; @@ -709,7 +668,7 @@ public static function findNodes(DOMDocument $dom, array $options, $isHtml = TRU foreach ($nodes as $node) { if ($parentNode !== $node->parentNode) { - break; + continue; } $filtered[] = $node; @@ -754,7 +713,7 @@ public static function findNodes(DOMDocument $dom, array $options, $isHtml = TRU foreach ($nodes as $node) { $parent = $node->parentNode; - while ($parent->nodeType != XML_HTML_DOCUMENT_NODE) { + while ($parent && $parent->nodeType != XML_HTML_DOCUMENT_NODE) { if ($parent === $ancestorNode) { $filtered[] = $node; } diff --git a/libs/PHPUnit/Text/Template.php b/libs/PHPUnit/Text/Template.php index f47b243..3a6ab84 100644 --- a/libs/PHPUnit/Text/Template.php +++ b/libs/PHPUnit/Text/Template.php @@ -2,7 +2,7 @@ /** * Text_Template * - * Copyright (c) 2009-2010, Sebastian Bergmann . + * Copyright (c) 2009-2012, Sebastian Bergmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,8 @@ * @category Text * @package Template * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ @@ -49,9 +49,9 @@ * @category Text * @package Template * @author Sebastian Bergmann - * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.1.0 + * @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 */ diff --git a/libs/PHPUnit/Text/Template/Autoload.php b/libs/PHPUnit/Text/Template/Autoload.php index 19776de..90ee111 100644 --- a/libs/PHPUnit/Text/Template/Autoload.php +++ b/libs/PHPUnit/Text/Template/Autoload.php @@ -38,28 +38,28 @@ * @package Template * @author Sebastian Bergmann * @copyright 2009-2010 Sebastian Bergmann - * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @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 */ -function text_template_autoload($class) { - static $classes = NULL; - static $path = NULL; +spl_autoload_register( + function ($class) { + static $classes = NULL; + static $path = NULL; - if ($classes === NULL) { - $classes = array( - 'text_template' => '/Template.php' - ); + if ($classes === NULL) { + $classes = array( + 'text_template' => '/Template.php' + ); - $path = dirname(dirname(__FILE__)); - } + $path = dirname(dirname(__FILE__)); + } - $cn = strtolower($class); + $cn = strtolower($class); - if (isset($classes[$cn])) { - require $path . $classes[$cn]; - } -} - -spl_autoload_register('text_template_autoload'); + if (isset($classes[$cn])) { + require $path . $classes[$cn]; + } + } +); diff --git a/libs/PHPUnit/Text/Template/Autoload.php.in b/libs/PHPUnit/Text/Template/Autoload.php.in new file mode 100644 index 0000000..1837403 --- /dev/null +++ b/libs/PHPUnit/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]; + } + } +); From 0f88fc29a80489bfbad74bef3df7772e72df22cb Mon Sep 17 00:00:00 2001 From: Vaclav Sir Date: Sat, 13 Oct 2012 14:06:52 +0200 Subject: [PATCH 2/3] Aktualizace na Nette 2.0.6 --- HttpPHPUnit/Main/TemplateFactory.php | 6 + .../StructureRenderer/StructureRenderer.php | 5 + libs/Nette/Application/Application.php | 560 ++-- .../Application/Diagnostics/RoutingPanel.php | 248 +- .../templates/RoutingPanel.panel.phtml | 213 +- .../templates/RoutingPanel.tab.phtml | 20 +- libs/Nette/Application/IPresenter.php | 63 +- libs/Nette/Application/IPresenterFactory.php | 80 +- libs/Nette/Application/IResponse.php | 64 +- libs/Nette/Application/IRouter.php | 89 +- libs/Nette/Application/MicroPresenter.php | 163 + libs/Nette/Application/PresenterFactory.php | 330 +- libs/Nette/Application/Request.php | 520 ++-- .../Application/Responses/FileResponse.php | 275 +- .../Application/Responses/ForwardResponse.php | 115 +- .../Application/Responses/JsonResponse.php | 163 +- .../Responses/RedirectResponse.php | 153 +- .../Application/Responses/TextResponse.php | 130 +- libs/Nette/Application/Routers/CliRouter.php | 253 +- libs/Nette/Application/Routers/Route.php | 1598 +++++----- libs/Nette/Application/Routers/RouteList.php | 278 +- .../Application/Routers/SimpleRouter.php | 306 +- .../Application/UI/BadSignalException.php | 56 +- libs/Nette/Application/UI/Control.php | 447 +-- libs/Nette/Application/UI/Form.php | 290 +- .../Application/UI/IPartiallyRenderable.php | 40 - libs/Nette/Application/UI/IRenderable.php | 77 +- libs/Nette/Application/UI/ISignalReceiver.php | 64 +- .../Nette/Application/UI/IStatePersistent.php | 80 +- .../Application/UI/InvalidLinkException.php | 61 +- libs/Nette/Application/UI/Link.php | 228 +- libs/Nette/Application/UI/Multiplier.php | 42 + libs/Nette/Application/UI/Presenter.php | 2763 +++++++++-------- .../Application/UI/PresenterComponent.php | 877 +++--- .../UI/PresenterComponentReflection.php | 293 +- libs/Nette/Application/exceptions.php | 170 +- libs/Nette/Application/templates/error.phtml | 69 +- libs/Nette/Caching/Cache.php | 796 ++--- libs/Nette/Caching/IStorage.php | 119 +- libs/Nette/Caching/OutputHelper.php | 117 +- .../Nette/Caching/Storages/DevNullStorage.php | 151 +- libs/Nette/Caching/Storages/FileJournal.php | 2394 +++++++------- libs/Nette/Caching/Storages/FileStorage.php | 832 ++--- libs/Nette/Caching/Storages/IJournal.php | 84 +- .../Caching/Storages/MemcachedStorage.php | 384 +-- libs/Nette/Caching/Storages/MemoryStorage.php | 171 +- .../Nette/Caching/Storages/PhpFileStorage.php | 118 +- libs/Nette/ComponentModel/Component.php | 693 +++-- libs/Nette/ComponentModel/Container.php | 513 ++- libs/Nette/ComponentModel/IComponent.php | 94 +- libs/Nette/ComponentModel/IContainer.php | 112 +- .../RecursiveComponentIterator.php | 116 +- libs/Nette/Config/Adapters/IniAdapter.php | 151 + libs/Nette/Config/Adapters/NeonAdapter.php | 93 + libs/Nette/Config/Adapters/PhpAdapter.php | 48 + libs/Nette/Config/Compiler.php | 367 +++ libs/Nette/Config/CompilerExtension.php | 130 + libs/Nette/Config/Config.php | 104 - libs/Nette/Config/Configurator.php | 342 ++ .../Config/Extensions/ConstantsExtension.php | 34 + .../Config/Extensions/NetteExtension.php | 371 +++ libs/Nette/Config/Extensions/PhpExtension.php | 55 + libs/Nette/Config/Helpers.php | 95 + libs/Nette/Config/IAdapter.php | 81 +- libs/Nette/Config/IniAdapter.php | 187 -- libs/Nette/Config/Loader.php | 139 + libs/Nette/Config/NeonAdapter.php | 98 - libs/Nette/DI/Container.php | 693 +++-- libs/Nette/DI/ContainerBuilder.php | 733 ++++- libs/Nette/DI/Diagnostics/ContainerPanel.php | 93 + .../templates/ContainerPanel.panel.phtml | 99 + .../templates/ContainerPanel.tab.phtml | 9 + libs/Nette/DI/Helpers.php | 160 + libs/Nette/DI/IContainer.php | 106 +- libs/Nette/DI/NestedAccessor.php | 101 + libs/Nette/DI/ServiceBuilder.php | 52 - libs/Nette/DI/ServiceDefinition.php | 134 + libs/Nette/DI/Statement.php | 39 + libs/Nette/DI/exceptions.php | 64 +- libs/Nette/Database/Connection.php | 617 ++-- .../Database/Diagnostics/ConnectionPanel.php | 321 +- libs/Nette/Database/Drivers/MsSqlDriver.php | 263 +- libs/Nette/Database/Drivers/MySqlDriver.php | 344 +- libs/Nette/Database/Drivers/OciDriver.php | 280 +- libs/Nette/Database/Drivers/OdbcDriver.php | 261 +- libs/Nette/Database/Drivers/PgSqlDriver.php | 351 ++- libs/Nette/Database/Drivers/Sqlite2Driver.php | 126 +- libs/Nette/Database/Drivers/SqliteDriver.php | 341 +- libs/Nette/Database/Helpers.php | 173 ++ libs/Nette/Database/IReflection.php | 68 + libs/Nette/Database/ISupplementalDriver.php | 170 +- .../Reflection/ConventionalReflection.php | 96 + .../Reflection/DatabaseReflection.php | 117 - .../Reflection/DiscoveredReflection.php | 196 ++ libs/Nette/Database/Row.php | 105 +- libs/Nette/Database/SqlLiteral.php | 78 +- libs/Nette/Database/SqlPreprocessor.php | 329 +- libs/Nette/Database/Statement.php | 446 ++- libs/Nette/Database/Table/ActiveRow.php | 581 ++-- .../Nette/Database/Table/GroupedSelection.php | 436 +-- libs/Nette/Database/Table/Selection.php | 1610 +++++----- libs/Nette/Database/Table/SqlBuilder.php | 388 +++ libs/Nette/Diagnostics/Bar.php | 154 +- libs/Nette/Diagnostics/BlueScreen.php | 261 +- libs/Nette/Diagnostics/Debugger.php | 1361 ++++---- libs/Nette/Diagnostics/DefaultBarPanel.php | 152 +- libs/Nette/Diagnostics/FireLogger.php | 376 +-- libs/Nette/Diagnostics/Helpers.php | 429 +-- libs/Nette/Diagnostics/IBarPanel.php | 76 +- libs/Nette/Diagnostics/Logger.php | 203 +- libs/Nette/Diagnostics/shortcuts.php | 25 + .../templates/bar.dumps.panel.phtml | 100 +- .../Diagnostics/templates/bar.dumps.tab.phtml | 28 +- .../templates/bar.errors.panel.phtml | 52 +- .../templates/bar.errors.tab.phtml | 30 +- .../templates/bar.memory.tab.phtml | 30 +- libs/Nette/Diagnostics/templates/bar.phtml | 723 ++++- .../Diagnostics/templates/bar.time.tab.phtml | 30 +- .../Diagnostics/templates/bluescreen.phtml | 976 ++++-- libs/Nette/Diagnostics/templates/error.phtml | 47 +- libs/Nette/Diagnostics/templates/netteQ.js | 664 ++-- libs/Nette/Forms/Container.php | 970 +++--- libs/Nette/Forms/ControlGroup.php | 248 +- libs/Nette/Forms/Controls/BaseControl.php | 1325 ++++---- libs/Nette/Forms/Controls/Button.php | 120 +- libs/Nette/Forms/Controls/Checkbox.php | 120 +- libs/Nette/Forms/Controls/HiddenField.php | 150 +- libs/Nette/Forms/Controls/ImageButton.php | 126 +- libs/Nette/Forms/Controls/MultiSelectBox.php | 221 +- libs/Nette/Forms/Controls/RadioList.php | 380 +-- libs/Nette/Forms/Controls/SelectBox.php | 489 ++- libs/Nette/Forms/Controls/SubmitButton.php | 248 +- libs/Nette/Forms/Controls/TextArea.php | 107 +- libs/Nette/Forms/Controls/TextBase.php | 539 ++-- libs/Nette/Forms/Controls/TextInput.php | 207 +- libs/Nette/Forms/Controls/UploadControl.php | 275 +- libs/Nette/Forms/Form.php | 1259 ++++---- libs/Nette/Forms/IControl.php | 140 +- libs/Nette/Forms/IFormRenderer.php | 65 +- libs/Nette/Forms/ISubmitterControl.php | 76 +- .../Forms/Rendering/DefaultFormRenderer.php | 1021 +++--- libs/Nette/Forms/Rule.php | 110 +- libs/Nette/Forms/Rules.php | 547 ++-- libs/Nette/Http/Context.php | 224 +- libs/Nette/Http/FileUpload.php | 434 +-- libs/Nette/Http/IRequest.php | 280 +- libs/Nette/Http/IResponse.php | 278 +- libs/Nette/Http/ISessionStorage.php | 76 +- libs/Nette/Http/IUser.php | 111 - libs/Nette/Http/Request.php | 741 ++--- libs/Nette/Http/RequestFactory.php | 504 +-- libs/Nette/Http/Response.php | 671 ++-- libs/Nette/Http/Session.php | 1144 +++---- ...essionNamespace.php => SessionSection.php} | 545 ++-- libs/Nette/Http/Url.php | 1052 +++---- libs/Nette/Http/UrlScript.php | 178 +- libs/Nette/Http/User.php | 403 --- libs/Nette/Http/UserStorage.php | 220 ++ libs/Nette/Iterators/CachingIterator.php | 526 ++-- libs/Nette/Iterators/Filter.php | 89 +- libs/Nette/Iterators/InstanceFilter.php | 124 +- libs/Nette/Iterators/Mapper.php | 89 +- libs/Nette/Iterators/RecursiveFilter.php | 127 +- libs/Nette/Iterators/Recursor.php | 120 +- libs/Nette/Latte/Compiler.php | 530 ++++ libs/Nette/Latte/DefaultMacros.php | 1100 ------- libs/Nette/Latte/Engine.php | 129 +- libs/Nette/Latte/HtmlNode.php | 102 +- libs/Nette/Latte/IMacro.php | 50 + libs/Nette/Latte/MacroNode.php | 154 +- libs/Nette/Latte/MacroTokenizer.php | 69 + libs/Nette/Latte/Macros/CacheMacro.php | 132 + libs/Nette/Latte/Macros/CoreMacros.php | 413 +++ libs/Nette/Latte/Macros/FormMacros.php | 137 + libs/Nette/Latte/Macros/MacroSet.php | 143 + libs/Nette/Latte/Macros/UIMacros.php | 520 ++++ libs/Nette/Latte/Parser.php | 1000 +++--- libs/Nette/Latte/PhpWriter.php | 319 ++ libs/Nette/Latte/Token.php | 53 + .../{ParseException.php => exceptions.php} | 53 +- libs/Nette/Loaders/AutoLoader.php | 190 +- libs/Nette/Loaders/NetteLoader.php | 391 +-- libs/Nette/Loaders/RobotLoader.php | 819 ++--- libs/Nette/Localization/ITranslator.php | 68 +- libs/Nette/Mail/IMailer.php | 65 +- libs/Nette/Mail/Message.php | 1005 +++--- libs/Nette/Mail/MimePart.php | 748 ++--- libs/Nette/Mail/SendmailMailer.php | 117 +- libs/Nette/Mail/SmtpMailer.php | 398 +-- libs/Nette/Reflection/Annotation.php | 88 +- libs/Nette/Reflection/AnnotationsParser.php | 713 +++-- libs/Nette/Reflection/ClassType.php | 661 ++-- libs/Nette/Reflection/Extension.php | 212 +- libs/Nette/Reflection/GlobalFunction.php | 308 +- libs/Nette/Reflection/IAnnotation.php | 56 +- libs/Nette/Reflection/Method.php | 503 ++- libs/Nette/Reflection/Parameter.php | 295 +- libs/Nette/Reflection/Property.php | 307 +- .../Security/AuthenticationException.php | 50 +- libs/Nette/Security/Diagnostics/UserPanel.php | 62 + .../templates/UserPanel.panel.phtml | 24 + .../Diagnostics/templates/UserPanel.tab.phtml | 12 + libs/Nette/Security/IAuthenticator.php | 88 +- libs/Nette/Security/IAuthorizator.php | 90 +- libs/Nette/Security/IIdentity.php | 76 +- libs/Nette/Security/IResource.php | 64 +- libs/Nette/Security/IRole.php | 64 +- libs/Nette/Security/IUserStorage.php | 72 + libs/Nette/Security/Identity.php | 349 +-- libs/Nette/Security/Permission.php | 1659 +++++----- libs/Nette/Security/SimpleAuthenticator.php | 122 +- libs/Nette/Security/User.php | 316 ++ libs/Nette/Templating/FileTemplate.php | 282 +- libs/Nette/Templating/FilterException.php | 96 +- .../{DefaultHelpers.php => Helpers.php} | 656 ++-- libs/Nette/Templating/IFileTemplate.php | 78 +- libs/Nette/Templating/ITemplate.php | 64 +- libs/Nette/Templating/Template.php | 896 +++--- libs/Nette/Utils/Arrays.php | 371 +-- libs/Nette/Utils/CriticalSection.php | 76 - libs/Nette/Utils/Finder.php | 842 ++--- libs/Nette/Utils/Html.php | 1216 ++++---- libs/Nette/Utils/Json.php | 192 +- libs/Nette/Utils/LimitedScope.php | 145 +- libs/Nette/Utils/MimeTypeDetector.php | 174 +- libs/Nette/Utils/Neon.php | 731 ++--- libs/Nette/Utils/Paginator.php | 500 +-- libs/Nette/Utils/PhpGenerator/ClassType.php | 145 + libs/Nette/Utils/PhpGenerator/Helpers.php | 201 ++ libs/Nette/Utils/PhpGenerator/Method.php | 139 + libs/Nette/Utils/PhpGenerator/Parameter.php | 52 + .../PhpGenerator/PhpLiteral.php} | 62 +- libs/Nette/Utils/PhpGenerator/Property.php | 52 + libs/Nette/Utils/SafeStream.php | 617 ++-- libs/Nette/Utils/Strings.php | 1055 ++++--- libs/Nette/Utils/Tokenizer.php | 537 ++-- libs/Nette/Utils/Validators.php | 288 ++ libs/Nette/common/ArrayHash.php | 236 +- libs/Nette/common/ArrayList.php | 232 +- libs/Nette/common/Callback.php | 376 +-- libs/Nette/common/Configurator.php | 509 --- libs/Nette/common/DateTime.php | 198 +- libs/Nette/common/Environment.php | 718 ++--- libs/Nette/common/Framework.php | 88 +- libs/Nette/common/FreezableObject.php | 152 +- libs/Nette/common/IFreezable.php | 76 +- libs/Nette/common/Image.php | 1241 ++++---- libs/Nette/common/Object.php | 347 ++- libs/Nette/common/ObjectMixin.php | 459 +-- libs/Nette/common/exceptions.php | 345 +- libs/Nette/loader.php | 143 +- libs/Nette/lockfile | 0 .../ResultPrinter/NetteDebug_call_Test.php | 2 +- 253 files changed, 45040 insertions(+), 36999 deletions(-) create mode 100644 libs/Nette/Application/MicroPresenter.php delete mode 100644 libs/Nette/Application/UI/IPartiallyRenderable.php create mode 100644 libs/Nette/Application/UI/Multiplier.php create mode 100644 libs/Nette/Config/Adapters/IniAdapter.php create mode 100644 libs/Nette/Config/Adapters/NeonAdapter.php create mode 100644 libs/Nette/Config/Adapters/PhpAdapter.php create mode 100644 libs/Nette/Config/Compiler.php create mode 100644 libs/Nette/Config/CompilerExtension.php delete mode 100644 libs/Nette/Config/Config.php create mode 100644 libs/Nette/Config/Configurator.php create mode 100644 libs/Nette/Config/Extensions/ConstantsExtension.php create mode 100644 libs/Nette/Config/Extensions/NetteExtension.php create mode 100644 libs/Nette/Config/Extensions/PhpExtension.php create mode 100644 libs/Nette/Config/Helpers.php delete mode 100644 libs/Nette/Config/IniAdapter.php create mode 100644 libs/Nette/Config/Loader.php delete mode 100644 libs/Nette/Config/NeonAdapter.php create mode 100644 libs/Nette/DI/Diagnostics/ContainerPanel.php create mode 100644 libs/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml create mode 100644 libs/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml create mode 100644 libs/Nette/DI/Helpers.php create mode 100644 libs/Nette/DI/NestedAccessor.php delete mode 100644 libs/Nette/DI/ServiceBuilder.php create mode 100644 libs/Nette/DI/ServiceDefinition.php create mode 100644 libs/Nette/DI/Statement.php create mode 100644 libs/Nette/Database/Helpers.php create mode 100644 libs/Nette/Database/IReflection.php create mode 100644 libs/Nette/Database/Reflection/ConventionalReflection.php delete mode 100644 libs/Nette/Database/Reflection/DatabaseReflection.php create mode 100644 libs/Nette/Database/Reflection/DiscoveredReflection.php create mode 100644 libs/Nette/Database/Table/SqlBuilder.php create mode 100644 libs/Nette/Diagnostics/shortcuts.php delete mode 100644 libs/Nette/Http/IUser.php rename libs/Nette/Http/{SessionNamespace.php => SessionSection.php} (77%) delete mode 100644 libs/Nette/Http/User.php create mode 100644 libs/Nette/Http/UserStorage.php create mode 100644 libs/Nette/Latte/Compiler.php delete mode 100644 libs/Nette/Latte/DefaultMacros.php create mode 100644 libs/Nette/Latte/IMacro.php create mode 100644 libs/Nette/Latte/MacroTokenizer.php create mode 100644 libs/Nette/Latte/Macros/CacheMacro.php create mode 100644 libs/Nette/Latte/Macros/CoreMacros.php create mode 100644 libs/Nette/Latte/Macros/FormMacros.php create mode 100644 libs/Nette/Latte/Macros/MacroSet.php create mode 100644 libs/Nette/Latte/Macros/UIMacros.php create mode 100644 libs/Nette/Latte/PhpWriter.php create mode 100644 libs/Nette/Latte/Token.php rename libs/Nette/Latte/{ParseException.php => exceptions.php} (62%) create mode 100644 libs/Nette/Security/Diagnostics/UserPanel.php create mode 100644 libs/Nette/Security/Diagnostics/templates/UserPanel.panel.phtml create mode 100644 libs/Nette/Security/Diagnostics/templates/UserPanel.tab.phtml create mode 100644 libs/Nette/Security/IUserStorage.php create mode 100644 libs/Nette/Security/User.php rename libs/Nette/Templating/{DefaultHelpers.php => Helpers.php} (53%) delete mode 100644 libs/Nette/Utils/CriticalSection.php create mode 100644 libs/Nette/Utils/PhpGenerator/ClassType.php create mode 100644 libs/Nette/Utils/PhpGenerator/Helpers.php create mode 100644 libs/Nette/Utils/PhpGenerator/Method.php create mode 100644 libs/Nette/Utils/PhpGenerator/Parameter.php rename libs/Nette/{DI/IServiceBuilder.php => Utils/PhpGenerator/PhpLiteral.php} (52%) create mode 100644 libs/Nette/Utils/PhpGenerator/Property.php create mode 100644 libs/Nette/Utils/Validators.php delete mode 100644 libs/Nette/common/Configurator.php delete mode 100644 libs/Nette/lockfile diff --git a/HttpPHPUnit/Main/TemplateFactory.php b/HttpPHPUnit/Main/TemplateFactory.php index a2e1812..f41b7a6 100644 --- a/HttpPHPUnit/Main/TemplateFactory.php +++ b/HttpPHPUnit/Main/TemplateFactory.php @@ -10,6 +10,12 @@ */ class TemplateFactory extends Control { + + public function templatePrepareFilters($template) + { + $template->registerFilter(new \Nette\Latte\Engine); + } + public static function create($file) { $control = new self; diff --git a/HttpPHPUnit/StructureRenderer/StructureRenderer.php b/HttpPHPUnit/StructureRenderer/StructureRenderer.php index a9a5ab5..9f24a13 100644 --- a/HttpPHPUnit/StructureRenderer/StructureRenderer.php +++ b/HttpPHPUnit/StructureRenderer/StructureRenderer.php @@ -110,4 +110,9 @@ private function loadMethod($path) return $result; } + public function templatePrepareFilters($template) + { + $template->registerFilter(new \Nette\Latte\Engine); + } + } diff --git a/libs/Nette/Application/Application.php b/libs/Nette/Application/Application.php index 9ed4bd7..5ad4cac 100644 --- a/libs/Nette/Application/Application.php +++ b/libs/Nette/Application/Application.php @@ -1,298 +1,262 @@ -context = $context; - } - - - - /** - * Dispatch a HTTP request to a front controller. - * @return void - */ - public function run() - { - $httpRequest = $this->context->httpRequest; - $httpResponse = $this->context->httpResponse; - - // check HTTP method - if ($this->allowedMethods) { - $method = $httpRequest->getMethod(); - if (!in_array($method, $this->allowedMethods, TRUE)) { - $httpResponse->setCode(Nette\Http\IResponse::S501_NOT_IMPLEMENTED); - $httpResponse->setHeader('Allow', implode(',', $this->allowedMethods)); - echo '

    Method ' . htmlSpecialChars($method) . ' is not implemented

    '; - return; - } - } - - // dispatching - $request = NULL; - $repeatedError = FALSE; - do { - try { - if (count($this->requests) > self::$maxLoop) { - throw new ApplicationException('Too many loops detected in application life cycle.'); - } - - if (!$request) { - $this->onStartup($this); - - // routing - $router = $this->getRouter(); - - // enable routing debugger - Diagnostics\RoutingPanel::initialize($this, $httpRequest); - - $request = $router->match($httpRequest); - if (!$request instanceof Request) { - $request = NULL; - throw new BadRequestException('No route for HTTP request.'); - } - - if (strcasecmp($request->getPresenterName(), $this->errorPresenter) === 0) { - throw new BadRequestException('Invalid request. Presenter is not achievable.'); - } - } - - $this->requests[] = $request; - $this->onRequest($this, $request); - - // Instantiate presenter - $presenterName = $request->getPresenterName(); - try { - $this->presenter = $this->getPresenterFactory()->createPresenter($presenterName); - } catch (InvalidPresenterException $e) { - throw new BadRequestException($e->getMessage(), 404, $e); - } - - $this->getPresenterFactory()->getPresenterClass($presenterName); - $request->setPresenterName($presenterName); - $request->freeze(); - - // Execute presenter - $response = $this->presenter->run($request); - $this->onResponse($this, $response); - - // Send response - if ($response instanceof Responses\ForwardResponse) { - $request = $response->getRequest(); - continue; - - } elseif ($response instanceof IResponse) { - $response->send($httpRequest, $httpResponse); - } - break; - - } catch (\Exception $e) { - // fault barrier - $this->onError($this, $e); - - if (!$this->catchExceptions) { - $this->onShutdown($this, $e); - throw $e; - } - - if ($repeatedError) { - $e = new ApplicationException('An error occurred while executing error-presenter', 0, $e); - } - - if (!$httpResponse->isSent()) { - $httpResponse->setCode($e instanceof BadRequestException ? $e->getCode() : 500); - } - - if (!$repeatedError && $this->errorPresenter) { - $repeatedError = TRUE; - if ($this->presenter instanceof UI\Presenter) { - try { - $this->presenter->forward(":$this->errorPresenter:", array('exception' => $e)); - } catch (AbortException $foo) { - $request = $this->presenter->getLastCreatedRequest(); - } - } else { - $request = new Request( - $this->errorPresenter, - Request::FORWARD, - array('exception' => $e) - ); - } - // continue - - } else { // default error handler - if ($e instanceof BadRequestException) { - $code = $e->getCode(); - } else { - $code = 500; - Nette\Diagnostics\Debugger::log($e, Nette\Diagnostics\Debugger::ERROR); - } - require __DIR__ . '/templates/error.phtml'; - break; - } - } - } while (1); - - $this->onShutdown($this, isset($e) ? $e : NULL); - } - - - - /** - * Returns all processed requests. - * @return array of Request - */ - final public function getRequests() - { - return $this->requests; - } - - - - /** - * Returns current presenter. - * @return IPresenter - */ - final public function getPresenter() - { - return $this->presenter; - } - - - - /********************* services ****************d*g**/ - - - - /** - * Gets the context. - * @return Nette\DI\IContainer - */ - final public function getContext() - { - return $this->context; - } - - - - /** - * Returns router. - * @return IRouter - */ - public function getRouter() - { - return $this->context->router; - } - - - - /** - * Returns presenter factory. - * @return IPresenterFactory - */ - public function getPresenterFactory() - { - return $this->context->presenterFactory; - } - - - - /********************* request serialization ****************d*g**/ - - - - /** - * Stores current request to session. - * @param mixed optional expiration time - * @return string key - */ - public function storeRequest($expiration = '+ 10 minutes') - { - $session = $this->context->session->getNamespace('Nette.Application/requests'); - do { - $key = Nette\Utils\Strings::random(5); - } while (isset($session[$key])); - - $session[$key] = end($this->requests); - $session->setExpiration($expiration, $key); - return $key; - } - - - - /** - * Restores current request to session. - * @param string key - * @return void - */ - public function restoreRequest($key) - { - $session = $this->context->session->getNamespace('Nette.Application/requests'); - if (isset($session[$key])) { - $request = clone $session[$key]; - unset($session[$key]); - $request->setFlag(Request::RESTORED, TRUE); - $this->presenter->sendResponse(new Responses\ForwardResponse($request)); - } - } - -} +httpRequest = $httpRequest; + $this->httpResponse = $httpResponse; + $this->presenterFactory = $presenterFactory; + $this->router = $router; + } + + + + /** + * Dispatch a HTTP request to a front controller. + * @return void + */ + public function run() + { + $request = NULL; + $repeatedError = FALSE; + do { + try { + if (count($this->requests) > self::$maxLoop) { + throw new ApplicationException('Too many loops detected in application life cycle.'); + } + + if (!$request) { + $this->onStartup($this); + + $request = $this->router->match($this->httpRequest); + if (!$request instanceof Request) { + $request = NULL; + throw new BadRequestException('No route for HTTP request.'); + } + + if (strcasecmp($request->getPresenterName(), $this->errorPresenter) === 0) { + throw new BadRequestException('Invalid request. Presenter is not achievable.'); + } + } + + $this->requests[] = $request; + $this->onRequest($this, $request); + + // Instantiate presenter + $presenterName = $request->getPresenterName(); + try { + $this->presenter = $this->presenterFactory->createPresenter($presenterName); + } catch (InvalidPresenterException $e) { + throw new BadRequestException($e->getMessage(), 404, $e); + } + + $this->presenterFactory->getPresenterClass($presenterName); + $request->setPresenterName($presenterName); + $request->freeze(); + + // Execute presenter + $response = $this->presenter->run($request); + if ($response) { + $this->onResponse($this, $response); + } + + // Send response + if ($response instanceof Responses\ForwardResponse) { + $request = $response->getRequest(); + continue; + + } elseif ($response instanceof IResponse) { + $response->send($this->httpRequest, $this->httpResponse); + } + break; + + } catch (\Exception $e) { + // fault barrier + $this->onError($this, $e); + + if (!$this->catchExceptions) { + $this->onShutdown($this, $e); + throw $e; + } + + if ($repeatedError) { + $e = new ApplicationException('An error occurred while executing error-presenter', 0, $e); + } + + if (!$this->httpResponse->isSent()) { + $this->httpResponse->setCode($e instanceof BadRequestException ? $e->getCode() : 500); + } + + if (!$repeatedError && $this->errorPresenter) { + $repeatedError = TRUE; + if ($this->presenter instanceof UI\Presenter) { + try { + $this->presenter->forward(":$this->errorPresenter:", array('exception' => $e)); + } catch (AbortException $foo) { + $request = $this->presenter->getLastCreatedRequest(); + } + } else { + $request = new Request( + $this->errorPresenter, + Request::FORWARD, + array('exception' => $e) + ); + } + // continue + + } else { // default error handler + if ($e instanceof BadRequestException) { + $code = $e->getCode(); + } else { + $code = 500; + Nette\Diagnostics\Debugger::log($e, Nette\Diagnostics\Debugger::ERROR); + } + require __DIR__ . '/templates/error.phtml'; + break; + } + } + } while (1); + + $this->onShutdown($this, isset($e) ? $e : NULL); + } + + + + /** + * Returns all processed requests. + * @return Request[] + */ + final public function getRequests() + { + return $this->requests; + } + + + + /** + * Returns current presenter. + * @return IPresenter + */ + final public function getPresenter() + { + return $this->presenter; + } + + + + /********************* services ****************d*g**/ + + + + /** + * Returns router. + * @return IRouter + */ + public function getRouter() + { + return $this->router; + } + + + + /** + * Returns presenter factory. + * @return IPresenterFactory + */ + public function getPresenterFactory() + { + return $this->presenterFactory; + } + + + + /********************* request serialization ****************d*g**/ + + + + /** @deprecated */ + function storeRequest($expiration = '+ 10 minutes') + { + return $this->presenter->storeRequest($expiration); + } + + /** @deprecated */ + function restoreRequest($key) + { + return $this->presenter->restoreRequest($key); + } + +} diff --git a/libs/Nette/Application/Diagnostics/RoutingPanel.php b/libs/Nette/Application/Diagnostics/RoutingPanel.php index c8e1c3f..147abf1 100644 --- a/libs/Nette/Application/Diagnostics/RoutingPanel.php +++ b/libs/Nette/Application/Diagnostics/RoutingPanel.php @@ -1,123 +1,125 @@ -addPanel(new self($application->getRouter(), $httpRequest)); - Debugger::$blueScreen->addPanel(function($e) use ($application) { - if ($e === NULL) { - return array( - 'tab' => 'Nette Application', - 'panel' => '

    Requests

    ' . Nette\Diagnostics\Helpers::clickableDump($application->getRequests()) - . '

    Presenter

    ' . Nette\Diagnostics\Helpers::clickableDump($application->getPresenter()) - ); - } - }); - } - - - - public function __construct(Nette\Application\IRouter $router, Nette\Http\IRequest $httpRequest) - { - $this->router = $router; - $this->httpRequest = $httpRequest; - } - - - - /** - * Renders tab. - * @return string - */ - public function getTab() - { - $this->analyse($this->router); - ob_start(); - require __DIR__ . '/templates/RoutingPanel.tab.phtml'; - return ob_get_clean(); - } - - - - /** - * Renders panel. - * @return string - */ - public function getPanel() - { - ob_start(); - require __DIR__ . '/templates/RoutingPanel.panel.phtml'; - return ob_get_clean(); - } - - - - /** - * Analyses simple route. - * @param Nette\Application\IRouter - * @return void - */ - private function analyse($router) - { - if ($router instanceof Routers\RouteList) { - foreach ($router as $subRouter) { - $this->analyse($subRouter); - } - return; - } - - $request = $router->match($this->httpRequest); - $matched = $request === NULL ? 'no' : 'may'; - if ($request !== NULL && empty($this->request)) { - $this->request = $request; - $matched = 'yes'; - } - - $this->routers[] = array( - 'matched' => $matched, - 'class' => get_class($router), - 'defaults' => $router instanceof Routers\Route || $router instanceof Routers\SimpleRouter ? $router->getDefaults() : array(), - 'mask' => $router instanceof Routers\Route ? $router->getMask() : NULL, - 'request' => $request, - ); - } - -} +addPanel(function($e) use ($application) { + return $e ? NULL : array( + 'tab' => 'Nette Application', + 'panel' => '

    Requests

    ' . Nette\Diagnostics\Helpers::clickableDump($application->getRequests()) + . '

    Presenter

    ' . Nette\Diagnostics\Helpers::clickableDump($application->getPresenter()) + ); + }); + } + + + + public function __construct(Nette\Application\IRouter $router, Nette\Http\IRequest $httpRequest) + { + $this->router = $router; + $this->httpRequest = $httpRequest; + } + + + + /** + * Renders tab. + * @return string + */ + public function getTab() + { + $this->analyse($this->router); + ob_start(); + require __DIR__ . '/templates/RoutingPanel.tab.phtml'; + return ob_get_clean(); + } + + + + /** + * Renders panel. + * @return string + */ + public function getPanel() + { + ob_start(); + require __DIR__ . '/templates/RoutingPanel.panel.phtml'; + return ob_get_clean(); + } + + + + /** + * Analyses simple route. + * @param Nette\Application\IRouter + * @return void + */ + private function analyse($router, $module = '') + { + if ($router instanceof Routers\RouteList) { + foreach ($router as $subRouter) { + $this->analyse($subRouter, $module . $router->getModule()); + } + return; + } + + $matched = 'no'; + $request = $router->match($this->httpRequest); + if ($request) { + $request->setPresenterName($module . $request->getPresenterName()); + $matched = 'may'; + if (empty($this->request)) { + $this->request = $request; + $matched = 'yes'; + } + } + + $this->routers[] = array( + 'matched' => $matched, + 'class' => get_class($router), + 'defaults' => $router instanceof Routers\Route || $router instanceof Routers\SimpleRouter ? $router->getDefaults() : array(), + 'mask' => $router instanceof Routers\Route ? $router->getMask() : NULL, + 'request' => $request, + 'module' => rtrim($module, ':') + ); + } + +} diff --git a/libs/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml b/libs/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml index 0817ec7..0ecf172 100644 --- a/libs/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml +++ b/libs/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml @@ -1,94 +1,119 @@ - - - -
    -

    -request)): ?> - no route - - request->getPresenterName() . ':' . (isset($this->request->params[Presenter::ACTION_KEY]) ? $this->request->params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->params[Presenter::SIGNAL_KEY]) ? " {$this->request->params[Presenter::SIGNAL_KEY]}!" : '') ?> - -

    - -request)): ?> - request->getParams() ?> - -

    No parameters.

    - - - - - - - - - - - - $value): ?> - - - - - - -
    ParameterValue
    - - - -

    Routers

    - -routers)): ?> -

    No routers defined.

    - - -
    - - - - - - - - - - - - - routers as $router): ?> - - - - - - - - - - - - - -
    Matched?ClassMaskDefaultsRequest
    - $value): ?> -
    - -
    - getParams(); ?> - getPresenterName() . ':' . (isset($params[Presenter::ACTION_KEY]) ? $params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION)) ?>
    - - $value): ?> -
    - -
    -
    - -
    + + + +
    +

    +request)): ?> + no route + + request->getPresenterName() . ':' . (isset($this->request->parameters[Presenter::ACTION_KEY]) ? $this->request->parameters[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->parameters[Presenter::SIGNAL_KEY]) ? " {$this->request->parameters[Presenter::SIGNAL_KEY]}!" : '')) ?> + +

    + +request)): ?> + request->getParameters() ?> + +

    No parameters.

    + + + + + + + + + + + + $value): ?> + + + + + + +
    ParameterValue
    + + + +

    Routers

    + +routers)): ?> +

    No routers defined.

    + + +
    + + + + + + + + + + + + + + routers as $router): ?> + + + + + + + + + + + + + + + +
    Matched?ClassMaskDefaultsModuleRequest
    + $value): ?> +
    + +
    + getParameters(); ?> + getPresenterName() . ':' . (isset($params[Presenter::ACTION_KEY]) ? $params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION)) ?>
    + + $value): ?> +
    + +
    +
    + +
    diff --git a/libs/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml b/libs/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml index c20bf31..9edf582 100644 --- a/libs/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml +++ b/libs/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml @@ -1,10 +1,10 @@ - -request)): ?>no routerequest->getPresenterName() . ':' . (isset($this->request->params[Presenter::ACTION_KEY]) ? $this->request->params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->params[Presenter::SIGNAL_KEY]) ? " {$this->request->params[Presenter::SIGNAL_KEY]}!" : ''); endif ?> + +request)): ?>no routerequest->getPresenterName() . ':' . (isset($this->request->parameters[Presenter::ACTION_KEY]) ? $this->request->parameters[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->parameters[Presenter::SIGNAL_KEY]) ? " {$this->request->parameters[Presenter::SIGNAL_KEY]}!" : '')); endif ?> diff --git a/libs/Nette/Application/IPresenter.php b/libs/Nette/Application/IPresenter.php index 4b0d686..f3aa80e 100644 --- a/libs/Nette/Application/IPresenter.php +++ b/libs/Nette/Application/IPresenter.php @@ -1,32 +1,31 @@ - - */ -interface IPresenterFactory -{ - - /** - * @param string presenter name - * @return string class name - * @throws InvalidPresenterException - */ - function getPresenterClass(& $name); - - /** - * Create new presenter instance. - * @param string presenter name - * @return IPresenter - */ - function createPresenter($name); - -} + + */ +interface IPresenterFactory +{ + + /** + * @param string presenter name + * @return string class name + * @throws InvalidPresenterException + */ + function getPresenterClass(& $name); + + /** + * Create new presenter instance. + * @param string presenter name + * @return IPresenter + */ + function createPresenter($name); + +} diff --git a/libs/Nette/Application/IResponse.php b/libs/Nette/Application/IResponse.php index 66ec800..63d9810 100644 --- a/libs/Nette/Application/IResponse.php +++ b/libs/Nette/Application/IResponse.php @@ -1,32 +1,32 @@ -context = $context; + } + + + + /** + * Gets the context. + * @return \SystemContainer|Nette\DI\Container + */ + final public function getContext() + { + return $this->context; + } + + + + /** + * @return Nette\Application\IResponse + */ + public function run(Application\Request $request) + { + $this->request = $request; + + $httpRequest = $this->context->getByType('Nette\Http\IRequest'); + if (!$httpRequest->isAjax() && ($request->isMethod('get') || $request->isMethod('head'))) { + $refUrl = clone $httpRequest->getUrl(); + $url = $this->context->router->constructUrl($request, $refUrl->setPath($refUrl->getScriptPath())); + if ($url !== NULL && !$httpRequest->getUrl()->isEqual($url)) { + return new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY); + } + } + + $params = $request->getParameters(); + if (!isset($params['callback'])) { + return; + } + $params['presenter'] = $this; + $callback = new Nette\Callback($params['callback']); + $response = $callback->invokeArgs(Application\UI\PresenterComponentReflection::combineArgs($callback->toReflection(), $params)); + + if (is_string($response)) { + $response = array($response, array()); + } + if (is_array($response)) { + if ($response[0] instanceof \SplFileInfo) { + $response = $this->createTemplate('Nette\Templating\FileTemplate') + ->setParameters($response[1])->setFile($response[0]); + } else { + $response = $this->createTemplate('Nette\Templating\Template') + ->setParameters($response[1])->setSource($response[0]); + } + } + if ($response instanceof Nette\Templating\ITemplate) { + return new Responses\TextResponse($response); + } else { + return $response; + } + } + + + + /** + * Template factory. + * @param string + * @param callable + * @return Nette\Templating\ITemplate + */ + public function createTemplate($class = NULL, $latteFactory = NULL) + { + $template = $class ? new $class : new Nette\Templating\FileTemplate; + + $template->setParameters($this->request->getParameters()); + $template->presenter = $this; + $template->context = $context = $this->context; + $url = $context->getByType('Nette\Http\IRequest')->getUrl(); + $template->baseUrl = rtrim($url->getBaseUrl(), '/'); + $template->basePath = rtrim($url->getBasePath(), '/'); + + $template->registerHelperLoader('Nette\Templating\Helpers::loader'); + $template->setCacheStorage($context->nette->templateCacheStorage); + $template->onPrepareFilters[] = function($template) use ($latteFactory, $context) { + $template->registerFilter($latteFactory ? $latteFactory() : new Nette\Latte\Engine); + }; + return $template; + } + + + + /** + * Redirects to another URL. + * @param string + * @param int HTTP code + * @return void + */ + public function redirectUrl($url, $code = Http\IResponse::S302_FOUND) + { + return new Responses\RedirectResponse($url, $code); + } + + + + /** + * Throws HTTP error. + * @param string + * @param int HTTP error code + * @return void + * @throws Nette\Application\BadRequestException + */ + public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND) + { + throw new Application\BadRequestException($message, $code); + } + + + + /** + * @return Nette\Application\IRequest + */ + public function getRequest() + { + return $this->request; + } + +} diff --git a/libs/Nette/Application/PresenterFactory.php b/libs/Nette/Application/PresenterFactory.php index 871fafa..bc0cbfe 100644 --- a/libs/Nette/Application/PresenterFactory.php +++ b/libs/Nette/Application/PresenterFactory.php @@ -1,159 +1,171 @@ -baseDir = $baseDir; - $this->context = $context; - } - - - - /** - * Create new presenter instance. - * @param string presenter name - * @return IPresenter - */ - public function createPresenter($name) - { - $class = $this->getPresenterClass($name); - $presenter = new $class; - $presenter->setContext($this->context); - return $presenter; - } - - - - /** - * @param string presenter name - * @return string class name - * @throws InvalidPresenterException - */ - public function getPresenterClass(& $name) - { - if (isset($this->cache[$name])) { - list($class, $name) = $this->cache[$name]; - return $class; - } - - if (!is_string($name) || !Nette\Utils\Strings::match($name, "#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#")) { - throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid."); - } - - $class = $this->formatPresenterClass($name); - - if (!class_exists($class)) { - // internal autoloading - $file = $this->formatPresenterFile($name); - if (is_file($file) && is_readable($file)) { - Nette\Utils\LimitedScope::load($file); - } - - if (!class_exists($class)) { - throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found in '$file'."); - } - } - - $reflection = new Nette\Reflection\ClassType($class); - $class = $reflection->getName(); - - if (!$reflection->implementsInterface('Nette\Application\IPresenter')) { - throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor."); - } - - if ($reflection->isAbstract()) { - throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract."); - } - - // canonicalize presenter name - $realName = $this->unformatPresenterClass($class); - if ($name !== $realName) { - if ($this->caseSensitive) { - throw new InvalidPresenterException("Cannot load presenter '$name', case mismatch. Real name is '$realName'."); - } else { - $this->cache[$name] = array($class, $realName); - $name = $realName; - } - } else { - $this->cache[$name] = array($class, $realName); - } - - return $class; - } - - - - /** - * Formats presenter class name from its name. - * @param string - * @return string - */ - public function formatPresenterClass($presenter) - { - return str_replace(':', 'Module\\', $presenter) . 'Presenter'; - } - - - - /** - * Formats presenter name from class name. - * @param string - * @return string - */ - public function unformatPresenterClass($class) - { - return str_replace('Module\\', ':', substr($class, 0, -9)); - } - - - - /** - * Formats presenter class file name. - * @param string - * @return string - */ - public function formatPresenterFile($presenter) - { - $path = '/' . str_replace(':', 'Module/', $presenter); - return $this->baseDir . substr_replace($path, '/presenters', strrpos($path, '/'), 0) . 'Presenter.php'; - } - -} +baseDir = $baseDir; + $this->container = $container; + } + + + + /** + * Create new presenter instance. + * @param string presenter name + * @return IPresenter + */ + public function createPresenter($name) + { + $presenter = $this->container->createInstance($this->getPresenterClass($name)); + if (method_exists($presenter, 'setContext')) { + $this->container->callMethod(array($presenter, 'setContext')); + } + foreach (array_reverse(get_class_methods($presenter)) as $method) { + if (substr($method, 0, 6) === 'inject') { + $this->container->callMethod(array($presenter, $method)); + } + } + + if ($presenter instanceof UI\Presenter && $presenter->invalidLinkMode === NULL) { + $presenter->invalidLinkMode = $this->container->parameters['debugMode'] ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT; + } + return $presenter; + } + + + + /** + * @param string presenter name + * @return string class name + * @throws InvalidPresenterException + */ + public function getPresenterClass(& $name) + { + if (isset($this->cache[$name])) { + list($class, $name) = $this->cache[$name]; + return $class; + } + + if (!is_string($name) || !Nette\Utils\Strings::match($name, "#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#")) { + throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid."); + } + + $class = $this->formatPresenterClass($name); + + if (!class_exists($class)) { + // internal autoloading + $file = $this->formatPresenterFile($name); + if (is_file($file) && is_readable($file)) { + Nette\Utils\LimitedScope::load($file, TRUE); + } + + if (!class_exists($class)) { + throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found in '$file'."); + } + } + + $reflection = new Nette\Reflection\ClassType($class); + $class = $reflection->getName(); + + if (!$reflection->implementsInterface('Nette\Application\IPresenter')) { + throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor."); + } + + if ($reflection->isAbstract()) { + throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract."); + } + + // canonicalize presenter name + $realName = $this->unformatPresenterClass($class); + if ($name !== $realName) { + if ($this->caseSensitive) { + throw new InvalidPresenterException("Cannot load presenter '$name', case mismatch. Real name is '$realName'."); + } else { + $this->cache[$name] = array($class, $realName); + $name = $realName; + } + } else { + $this->cache[$name] = array($class, $realName); + } + + return $class; + } + + + + /** + * Formats presenter class name from its name. + * @param string + * @return string + */ + public function formatPresenterClass($presenter) + { + /*5.2*return strtr($presenter, ':', '_') . 'Presenter';*/ + return str_replace(':', 'Module\\', $presenter) . 'Presenter'; + } + + + + /** + * Formats presenter name from class name. + * @param string + * @return string + */ + public function unformatPresenterClass($class) + { + /*5.2*return strtr(substr($class, 0, -9), '_', ':');*/ + return str_replace('Module\\', ':', substr($class, 0, -9)); + } + + + + /** + * Formats presenter class file name. + * @param string + * @return string + */ + public function formatPresenterFile($presenter) + { + $path = '/' . str_replace(':', 'Module/', $presenter); + return $this->baseDir . substr_replace($path, '/presenters', strrpos($path, '/'), 0) . 'Presenter.php'; + } + +} diff --git a/libs/Nette/Application/Request.php b/libs/Nette/Application/Request.php index 4da25b4..b4b6c4a 100644 --- a/libs/Nette/Application/Request.php +++ b/libs/Nette/Application/Request.php @@ -1,250 +1,270 @@ -name = $name; - $this->method = $method; - $this->params = $params; - $this->post = $post; - $this->files = $files; - $this->flags = $flags; - } - - - - /** - * Sets the presenter name. - * @param string - * @return Request provides a fluent interface - */ - public function setPresenterName($name) - { - $this->updating(); - $this->name = $name; - return $this; - } - - - - /** - * Retrieve the presenter name. - * @return string - */ - public function getPresenterName() - { - return $this->name; - } - - - - /** - * Sets variables provided to the presenter. - * @param array - * @return Request provides a fluent interface - */ - public function setParams(array $params) - { - $this->updating(); - $this->params = $params; - return $this; - } - - - - /** - * Returns all variables provided to the presenter (usually via URL). - * @return array - */ - public function getParams() - { - return $this->params; - } - - - - /** - * Sets variables provided to the presenter via POST. - * @param array - * @return Request provides a fluent interface - */ - public function setPost(array $params) - { - $this->updating(); - $this->post = $params; - return $this; - } - - - - /** - * Returns all variables provided to the presenter via POST. - * @return array - */ - public function getPost() - { - return $this->post; - } - - - - /** - * Sets all uploaded files. - * @param array - * @return Request provides a fluent interface - */ - public function setFiles(array $files) - { - $this->updating(); - $this->files = $files; - return $this; - } - - - - /** - * Returns all uploaded files. - * @return array - */ - public function getFiles() - { - return $this->files; - } - - - - /** - * Sets the method. - * @param string - * @return Request provides a fluent interface - */ - public function setMethod($method) - { - $this->method = $method; - return $this; - } - - - - /** - * Returns the method. - * @return string - */ - public function getMethod() - { - return $this->method; - } - - - - /** - * Checks if the method is the given one. - * @param string - * @return bool - */ - public function isMethod($method) - { - return strcasecmp($this->method, $method) === 0; - } - - - - /** - * Checks if the method is POST. - * @return bool - */ - public function isPost() - { - return strcasecmp($this->method, 'post') === 0; - } - - - - /** - * Sets the flag. - * @param string - * @param bool - * @return Request provides a fluent interface - */ - public function setFlag($flag, $value = TRUE) - { - $this->updating(); - $this->flags[$flag] = (bool) $value; - return $this; - } - - - - /** - * Checks the flag. - * @param string - * @return bool - */ - public function hasFlag($flag) - { - return !empty($this->flags[$flag]); - } - -} +name = $name; + $this->method = $method; + $this->params = $params; + $this->post = $post; + $this->files = $files; + $this->flags = $flags; + } + + + + /** + * Sets the presenter name. + * @param string + * @return Request provides a fluent interface + */ + public function setPresenterName($name) + { + $this->updating(); + $this->name = $name; + return $this; + } + + + + /** + * Retrieve the presenter name. + * @return string + */ + public function getPresenterName() + { + return $this->name; + } + + + + /** + * Sets variables provided to the presenter. + * @param array + * @return Request provides a fluent interface + */ + public function setParameters(array $params) + { + $this->updating(); + $this->params = $params; + return $this; + } + + + + /** + * Returns all variables provided to the presenter (usually via URL). + * @return array + */ + public function getParameters() + { + return $this->params; + } + + + + /** @deprecated */ + function setParams(array $params) + { + trigger_error(__METHOD__ . '() is deprecated; use setParameters() instead.', E_USER_WARNING); + return $this->setParameters($params); + } + + + + /** @deprecated */ + function getParams() + { + trigger_error(__METHOD__ . '() is deprecated; use getParameters() instead.', E_USER_WARNING); + return $this->getParameters(); + } + + + + /** + * Sets variables provided to the presenter via POST. + * @param array + * @return Request provides a fluent interface + */ + public function setPost(array $params) + { + $this->updating(); + $this->post = $params; + return $this; + } + + + + /** + * Returns all variables provided to the presenter via POST. + * @return array + */ + public function getPost() + { + return $this->post; + } + + + + /** + * Sets all uploaded files. + * @param array + * @return Request provides a fluent interface + */ + public function setFiles(array $files) + { + $this->updating(); + $this->files = $files; + return $this; + } + + + + /** + * Returns all uploaded files. + * @return array + */ + public function getFiles() + { + return $this->files; + } + + + + /** + * Sets the method. + * @param string + * @return Request provides a fluent interface + */ + public function setMethod($method) + { + $this->method = $method; + return $this; + } + + + + /** + * Returns the method. + * @return string + */ + public function getMethod() + { + return $this->method; + } + + + + /** + * Checks if the method is the given one. + * @param string + * @return bool + */ + public function isMethod($method) + { + return strcasecmp($this->method, $method) === 0; + } + + + + /** + * Checks if the method is POST. + * @return bool + */ + public function isPost() + { + return strcasecmp($this->method, 'post') === 0; + } + + + + /** + * Sets the flag. + * @param string + * @param bool + * @return Request provides a fluent interface + */ + public function setFlag($flag, $value = TRUE) + { + $this->updating(); + $this->flags[$flag] = (bool) $value; + return $this; + } + + + + /** + * Checks the flag. + * @param string + * @return bool + */ + public function hasFlag($flag) + { + return !empty($this->flags[$flag]); + } + +} diff --git a/libs/Nette/Application/Responses/FileResponse.php b/libs/Nette/Application/Responses/FileResponse.php index 6c25592..857064b 100644 --- a/libs/Nette/Application/Responses/FileResponse.php +++ b/libs/Nette/Application/Responses/FileResponse.php @@ -1,136 +1,139 @@ -file = $file; - $this->name = $name ? $name : basename($file); - $this->contentType = $contentType ? $contentType : 'application/octet-stream'; - } - - - - /** - * Returns the path to a downloaded file. - * @return string - */ - final public function getFile() - { - return $this->file; - } - - - - /** - * Returns the file name. - * @return string - */ - final public function getName() - { - return $this->name; - } - - - - /** - * Returns the MIME content type of a downloaded file. - * @return string - */ - final public function getContentType() - { - return $this->contentType; - } - - - - /** - * Sends response to output. - * @return void - */ - public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) - { - $httpResponse->setContentType($this->contentType); - $httpResponse->setHeader('Content-Disposition', 'attachment; filename="' . $this->name . '"'); - - $filesize = $length = filesize($this->file); - $handle = fopen($this->file, 'r'); - - if ($this->resuming) { - $httpResponse->setHeader('Accept-Ranges', 'bytes'); - $range = $httpRequest->getHeader('Range'); - if ($range !== NULL) { - $range = substr($range, 6); // 6 == strlen('bytes=') - list($start, $end) = explode('-', $range); - if ($start == NULL) { - $start = 0; - } - if ($end == NULL) { - $end = $filesize - 1; - } - - if ($start < 0 || $end <= $start || $end > $filesize -1) { - $httpResponse->setCode(416); // requested range not satisfiable - return; - } - - $httpResponse->setCode(206); - $httpResponse->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $filesize); - $length = $end - $start + 1; - fseek($handle, $start); - - } else { - $httpResponse->setHeader('Content-Range', 'bytes 0-' . ($filesize - 1) . '/' . $filesize); - } - } - - $httpResponse->setHeader('Content-Length', $length); - while (!feof($handle)) { - echo fread($handle, 4e6); - } - fclose($handle); - } - -} +file = $file; + $this->name = $name ? $name : basename($file); + $this->contentType = $contentType ? $contentType : 'application/octet-stream'; + } + + + + /** + * Returns the path to a downloaded file. + * @return string + */ + final public function getFile() + { + return $this->file; + } + + + + /** + * Returns the file name. + * @return string + */ + final public function getName() + { + return $this->name; + } + + + + /** + * Returns the MIME content type of a downloaded file. + * @return string + */ + final public function getContentType() + { + return $this->contentType; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + $httpResponse->setContentType($this->contentType); + $httpResponse->setHeader('Content-Disposition', 'attachment; filename="' . $this->name . '"'); + + $filesize = $length = filesize($this->file); + $handle = fopen($this->file, 'r'); + + if ($this->resuming) { + $httpResponse->setHeader('Accept-Ranges', 'bytes'); + if (preg_match('#^bytes=(\d*)-(\d*)$#', $httpRequest->getHeader('Range'), $matches)) { + list(, $start, $end) = $matches; + if ($start === '') { + $start = max(0, $filesize - $end); + $end = $filesize - 1; + + } elseif ($end === '' || $end > $filesize - 1) { + $end = $filesize - 1; + } + if ($end < $start) { + $httpResponse->setCode(416); // requested range not satisfiable + return; + } + + $httpResponse->setCode(206); + $httpResponse->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $filesize); + $length = $end - $start + 1; + fseek($handle, $start); + + } else { + $httpResponse->setHeader('Content-Range', 'bytes 0-' . ($filesize - 1) . '/' . $filesize); + } + } + + $httpResponse->setHeader('Content-Length', $length); + while (!feof($handle) && $length > 0) { + echo $s = fread($handle, min(4e6, $length)); + $length -= strlen($s); + } + fclose($handle); + } + +} diff --git a/libs/Nette/Application/Responses/ForwardResponse.php b/libs/Nette/Application/Responses/ForwardResponse.php index ba2a5bc..de04e6d 100644 --- a/libs/Nette/Application/Responses/ForwardResponse.php +++ b/libs/Nette/Application/Responses/ForwardResponse.php @@ -1,58 +1,57 @@ -request = $request; - } - - - - /** - * @return Nette\Application\Request - */ - final public function getRequest() - { - return $this->request; - } - - - - /** - * Sends response to output. - * @return void - */ - public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) - { - } - -} +request = $request; + } + + + + /** + * @return Nette\Application\Request + */ + final public function getRequest() + { + return $this->request; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + } + +} diff --git a/libs/Nette/Application/Responses/JsonResponse.php b/libs/Nette/Application/Responses/JsonResponse.php index 8c322cf..1002de9 100644 --- a/libs/Nette/Application/Responses/JsonResponse.php +++ b/libs/Nette/Application/Responses/JsonResponse.php @@ -1,80 +1,83 @@ -payload = $payload; - $this->contentType = $contentType ? $contentType : 'application/json'; - } - - - - /** - * @return array|stdClass - */ - final public function getPayload() - { - return $this->payload; - } - - - - /** - * Returns the MIME content type of a downloaded file. - * @return string - */ - final public function getContentType() - { - return $this->contentType; - } - - - - /** - * Sends response to output. - * @return void - */ - public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) - { - $httpResponse->setContentType($this->contentType); - $httpResponse->setExpiration(FALSE); - echo Nette\Utils\Json::encode($this->payload); - } - -} +payload = $payload; + $this->contentType = $contentType ? $contentType : 'application/json'; + } + + + + /** + * @return array|\stdClass + */ + final public function getPayload() + { + return $this->payload; + } + + + + /** + * Returns the MIME content type of a downloaded file. + * @return string + */ + final public function getContentType() + { + return $this->contentType; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + $httpResponse->setContentType($this->contentType); + $httpResponse->setExpiration(FALSE); + echo Nette\Utils\Json::encode($this->payload); + } + +} diff --git a/libs/Nette/Application/Responses/RedirectResponse.php b/libs/Nette/Application/Responses/RedirectResponse.php index 942c834..92d7ce9 100644 --- a/libs/Nette/Application/Responses/RedirectResponse.php +++ b/libs/Nette/Application/Responses/RedirectResponse.php @@ -1,75 +1,78 @@ -url = (string) $url; - $this->code = (int) $code; - } - - - - /** - * @return string - */ - final public function getUrl() - { - return $this->url; - } - - - - /** - * @return int - */ - final public function getCode() - { - return $this->code; - } - - - - /** - * Sends response to output. - * @return void - */ - public function send(Http\IRequest $httpRequest, Http\IResponse $httpResponse) - { - $httpResponse->redirect($this->url, $this->code); - } - -} +url = (string) $url; + $this->code = (int) $code; + } + + + + /** + * @return string + */ + final public function getUrl() + { + return $this->url; + } + + + + /** + * @return int + */ + final public function getCode() + { + return $this->code; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Http\IRequest $httpRequest, Http\IResponse $httpResponse) + { + $httpResponse->redirect($this->url, $this->code); + } + +} diff --git a/libs/Nette/Application/Responses/TextResponse.php b/libs/Nette/Application/Responses/TextResponse.php index 6c63d7b..dbd3138 100644 --- a/libs/Nette/Application/Responses/TextResponse.php +++ b/libs/Nette/Application/Responses/TextResponse.php @@ -1,64 +1,66 @@ -source = $source; - } - - - - /** - * @return mixed - */ - final public function getSource() - { - return $this->source; - } - - - - /** - * Sends response to output. - * @return void - */ - public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) - { - if ($this->source instanceof Nette\Templating\ITemplate) { - $this->source->render(); - - } else { - echo $this->source; - } - } - -} +source = $source; + } + + + + /** + * @return mixed + */ + final public function getSource() + { + return $this->source; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + if ($this->source instanceof Nette\Templating\ITemplate) { + $this->source->render(); + + } else { + echo $this->source; + } + } + +} diff --git a/libs/Nette/Application/Routers/CliRouter.php b/libs/Nette/Application/Routers/CliRouter.php index 0b4bf1d..8f894a2 100644 --- a/libs/Nette/Application/Routers/CliRouter.php +++ b/libs/Nette/Application/Routers/CliRouter.php @@ -1,127 +1,126 @@ -defaults = $defaults; - } - - - - /** - * Maps command line arguments to a Request object. - * @param Nette\Http\IRequest - * @return Nette\Application\Request|NULL - */ - public function match(Nette\Http\IRequest $httpRequest) - { - if (empty($_SERVER['argv']) || !is_array($_SERVER['argv'])) { - return NULL; - } - - $names = array(self::PRESENTER_KEY); - $params = $this->defaults; - $args = $_SERVER['argv']; - array_shift($args); - $args[] = '--'; - - foreach ($args as $arg) { - $opt = preg_replace('#/|-+#A', '', $arg); - if ($opt === $arg) { - if (isset($flag) || $flag = array_shift($names)) { - $params[$flag] = $arg; - } else { - $params[] = $arg; - } - $flag = NULL; - continue; - } - - if (isset($flag)) { - $params[$flag] = TRUE; - $flag = NULL; - } - - if ($opt !== '') { - $pair = explode('=', $opt, 2); - if (isset($pair[1])) { - $params[$pair[0]] = $pair[1]; - } else { - $flag = $pair[0]; - } - } - } - - if (!isset($params[self::PRESENTER_KEY])) { - throw new Nette\InvalidStateException('Missing presenter & action in route definition.'); - } - $presenter = $params[self::PRESENTER_KEY]; - if ($a = strrpos($presenter, ':')) { - $params[self::PRESENTER_KEY] = substr($presenter, $a + 1); - $presenter = substr($presenter, 0, $a); - } - - return new Application\Request( - $presenter, - 'CLI', - $params - ); - } - - - - /** - * This router is only unidirectional. - * @param Nette\Application\Request - * @param Nette\Http\Url - * @return NULL - */ - public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) - { - return NULL; - } - - - - /** - * Returns default values. - * @return array - */ - public function getDefaults() - { - return $this->defaults; - } - -} +defaults = $defaults; + } + + + + /** + * Maps command line arguments to a Request object. + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + if (empty($_SERVER['argv']) || !is_array($_SERVER['argv'])) { + return NULL; + } + + $names = array(self::PRESENTER_KEY); + $params = $this->defaults; + $args = $_SERVER['argv']; + array_shift($args); + $args[] = '--'; + + foreach ($args as $arg) { + $opt = preg_replace('#/|-+#A', '', $arg); + if ($opt === $arg) { + if (isset($flag) || $flag = array_shift($names)) { + $params[$flag] = $arg; + } else { + $params[] = $arg; + } + $flag = NULL; + continue; + } + + if (isset($flag)) { + $params[$flag] = TRUE; + $flag = NULL; + } + + if ($opt !== '') { + $pair = explode('=', $opt, 2); + if (isset($pair[1])) { + $params[$pair[0]] = $pair[1]; + } else { + $flag = $pair[0]; + } + } + } + + if (!isset($params[self::PRESENTER_KEY])) { + throw new Nette\InvalidStateException('Missing presenter & action in route definition.'); + } + $presenter = $params[self::PRESENTER_KEY]; + if ($a = strrpos($presenter, ':')) { + $params[self::PRESENTER_KEY] = substr($presenter, $a + 1); + $presenter = substr($presenter, 0, $a); + } + + return new Application\Request( + $presenter, + 'CLI', + $params + ); + } + + + + /** + * This router is only unidirectional. + * @return NULL + */ + public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) + { + return NULL; + } + + + + /** + * Returns default values. + * @return array + */ + public function getDefaults() + { + return $this->defaults; + } + +} diff --git a/libs/Nette/Application/Routers/Route.php b/libs/Nette/Application/Routers/Route.php index 24c6510..68842ce 100644 --- a/libs/Nette/Application/Routers/Route.php +++ b/libs/Nette/Application/Routers/Route.php @@ -1,776 +1,822 @@ - array( // default style for path parameters - self::PATTERN => '[^/]+', - self::FILTER_IN => 'rawurldecode', - self::FILTER_OUT => 'rawurlencode', - ), - '?#' => array( // default style for query parameters - ), - 'module' => array( - self::PATTERN => '[a-z][a-z0-9.-]*', - self::FILTER_IN => array(__CLASS__, 'path2presenter'), - self::FILTER_OUT => array(__CLASS__, 'presenter2path'), - ), - 'presenter' => array( - self::PATTERN => '[a-z][a-z0-9.-]*', - self::FILTER_IN => array(__CLASS__, 'path2presenter'), - self::FILTER_OUT => array(__CLASS__, 'presenter2path'), - ), - 'action' => array( - self::PATTERN => '[a-z][a-z0-9-]*', - self::FILTER_IN => array(__CLASS__, 'path2action'), - self::FILTER_OUT => array(__CLASS__, 'action2path'), - ), - '?module' => array( - ), - '?presenter' => array( - ), - '?action' => array( - ), - ); - - /** @var string */ - private $mask; - - /** @var array */ - private $sequence; - - /** @var string regular expression pattern */ - private $re; - - /** @var array of [value & fixity, filterIn, filterOut] */ - private $metadata = array(); - - /** @var array */ - private $xlat; - - /** @var int HOST, PATH, RELATIVE */ - private $type; - - /** @var int */ - private $flags; - - - - /** - * @param string URL mask, e.g. '//' - * @param array|string default values or metadata - * @param int flags - */ - public function __construct($mask, $metadata = array(), $flags = 0) - { - if (is_string($metadata)) { - $a = strrpos($metadata, ':'); - if (!$a) { - throw new Nette\InvalidArgumentException("Second argument must be array or string in format Presenter:action, '$metadata' given."); - } - $metadata = array( - self::PRESENTER_KEY => substr($metadata, 0, $a), - 'action' => $a === strlen($metadata) - 1 ? Application\UI\Presenter::DEFAULT_ACTION : substr($metadata, $a + 1), - ); - } - - $this->flags = $flags | self::$defaultFlags; - $this->setMask($mask, $metadata); - } - - - - /** - * Maps HTTP request to a Request object. - * @param Nette\Http\IRequest - * @return Nette\Application\Request|NULL - */ - public function match(Nette\Http\IRequest $httpRequest) - { - // combine with precedence: mask (params in URL-path), fixity, query, (post,) defaults - - // 1) URL MASK - $url = $httpRequest->getUrl(); - - if ($this->type === self::HOST) { - $path = '//' . $url->getHost() . $url->getPath(); - - } elseif ($this->type === self::RELATIVE) { - $basePath = $url->getBasePath(); - if (strncmp($url->getPath(), $basePath, strlen($basePath)) !== 0) { - return NULL; - } - $path = (string) substr($url->getPath(), strlen($basePath)); - - } else { - $path = $url->getPath(); - } - - if ($path !== '') { - $path = rtrim($path, '/') . '/'; - } - - if (!$matches = Strings::match($path, $this->re)) { - // stop, not matched - return NULL; - } - - // deletes numeric keys, restore '-' chars - $params = array(); - foreach ($matches as $k => $v) { - if (is_string($k) && $v !== '') { - $params[str_replace('___', '-', $k)] = $v; // trick - } - } - - - // 2) CONSTANT FIXITY - foreach ($this->metadata as $name => $meta) { - if (isset($params[$name])) { - //$params[$name] = $this->flags & self::CASE_SENSITIVE === 0 ? strtolower($params[$name]) : */$params[$name]; // strtolower damages UTF-8 - - } elseif (isset($meta['fixity']) && $meta['fixity'] !== self::OPTIONAL) { - $params[$name] = NULL; // cannot be overwriten in 3) and detected by isset() in 4) - } - } - - - // 3) QUERY - if ($this->xlat) { - $params += self::renameKeys($httpRequest->getQuery(), array_flip($this->xlat)); - } else { - $params += $httpRequest->getQuery(); - } - - - // 4) APPLY FILTERS & FIXITY - foreach ($this->metadata as $name => $meta) { - if (isset($params[$name])) { - if (!is_scalar($params[$name])) { - - } elseif (isset($meta[self::FILTER_TABLE][$params[$name]])) { // applyies filterTable only to scalar parameters - $params[$name] = $meta[self::FILTER_TABLE][$params[$name]]; - - } elseif (isset($meta[self::FILTER_IN])) { // applyies filterIn only to scalar parameters - $params[$name] = call_user_func($meta[self::FILTER_IN], (string) $params[$name]); - if ($params[$name] === NULL && !isset($meta['fixity'])) { - return NULL; // rejected by filter - } - } - - } elseif (isset($meta['fixity'])) { - $params[$name] = $meta[self::VALUE]; - } - } - - - // 5) BUILD Request - if (!isset($params[self::PRESENTER_KEY])) { - throw new Nette\InvalidStateException('Missing presenter in route definition.'); - } - if (isset($this->metadata[self::MODULE_KEY])) { - if (!isset($params[self::MODULE_KEY])) { - throw new Nette\InvalidStateException('Missing module in route definition.'); - } - $presenter = $params[self::MODULE_KEY] . ':' . $params[self::PRESENTER_KEY]; - unset($params[self::MODULE_KEY], $params[self::PRESENTER_KEY]); - - } else { - $presenter = $params[self::PRESENTER_KEY]; - unset($params[self::PRESENTER_KEY]); - } - - return new Application\Request( - $presenter, - $httpRequest->getMethod(), - $params, - $httpRequest->getPost(), - $httpRequest->getFiles(), - array(Application\Request::SECURED => $httpRequest->isSecured()) - ); - } - - - - /** - * Constructs absolute URL from Request object. - * @param Nette\Application\Request - * @param Nette\Http\Url - * @return string|NULL - */ - public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) - { - if ($this->flags & self::ONE_WAY) { - return NULL; - } - - $params = $appRequest->getParams(); - $metadata = $this->metadata; - - $presenter = $appRequest->getPresenterName(); - $params[self::PRESENTER_KEY] = $presenter; - - if (isset($metadata[self::MODULE_KEY])) { // try split into module and [submodule:]presenter parts - $module = $metadata[self::MODULE_KEY]; - if (isset($module['fixity']) && strncasecmp($presenter, $module[self::VALUE] . ':', strlen($module[self::VALUE]) + 1) === 0) { - $a = strlen($module[self::VALUE]); - } else { - $a = strrpos($presenter, ':'); - } - if ($a === FALSE) { - $params[self::MODULE_KEY] = ''; - } else { - $params[self::MODULE_KEY] = substr($presenter, 0, $a); - $params[self::PRESENTER_KEY] = substr($presenter, $a + 1); - } - } - - foreach ($metadata as $name => $meta) { - if (!isset($params[$name])) { - continue; // retains NULL values - } - - if (isset($meta['fixity'])) { - if (is_scalar($params[$name]) && strcasecmp($params[$name], $meta[self::VALUE]) === 0) { - // remove default values; NULL values are retain - unset($params[$name]); - continue; - - } elseif ($meta['fixity'] === self::CONSTANT) { - return NULL; // missing or wrong parameter '$name' - } - } - - if (!is_scalar($params[$name])) { - - } elseif (isset($meta['filterTable2'][$params[$name]])) { - $params[$name] = $meta['filterTable2'][$params[$name]]; - - } elseif (isset($meta[self::FILTER_OUT])) { - $params[$name] = call_user_func($meta[self::FILTER_OUT], $params[$name]); - } - - if (isset($meta[self::PATTERN]) && !preg_match($meta[self::PATTERN], rawurldecode($params[$name]))) { - return NULL; // pattern not match - } - } - - // compositing path - $sequence = $this->sequence; - $brackets = array(); - $required = 0; - $url = ''; - $i = count($sequence) - 1; - do { - $url = $sequence[$i] . $url; - if ($i === 0) { - break; - } - $i--; - - $name = $sequence[$i]; $i--; // parameter name - - if ($name === ']') { // opening optional part - $brackets[] = $url; - - } elseif ($name[0] === '[') { // closing optional part - $tmp = array_pop($brackets); - if ($required < count($brackets) + 1) { // is this level optional? - if ($name !== '[!') { // and not "required"-optional - $url = $tmp; - } - } else { - $required = count($brackets); - } - - } elseif ($name[0] === '?') { // "foo" parameter - continue; - - } elseif (isset($params[$name]) && $params[$name] != '') { // intentionally == - $required = count($brackets); // make this level required - $url = $params[$name] . $url; - unset($params[$name]); - - } elseif (isset($metadata[$name]['fixity'])) { // has default value? - $url = $metadata[$name]['defOut'] . $url; - - } else { - return NULL; // missing parameter '$name' - } - } while (TRUE); - - - // build query string - if ($this->xlat) { - $params = self::renameKeys($params, $this->xlat); - } - - $sep = ini_get('arg_separator.input'); - $query = http_build_query($params, '', $sep ? $sep[0] : '&'); - if ($query != '') { // intentionally == - $url .= '?' . $query; - } - - // absolutize path - if ($this->type === self::RELATIVE) { - $url = '//' . $refUrl->getAuthority() . $refUrl->getBasePath() . $url; - - } elseif ($this->type === self::PATH) { - $url = '//' . $refUrl->getAuthority() . $url; - } - - if (strpos($url, '//', 2) !== FALSE) { - return NULL; // TODO: implement counterpart in match() ? - } - - $url = ($this->flags & self::SECURED ? 'https:' : 'http:') . $url; - - return $url; - } - - - - /** - * Parse mask and array of default values; initializes object. - * @param string - * @param array - * @return void - */ - private function setMask($mask, array $metadata) - { - $this->mask = $mask; - - // detect '//host/path' vs. '/abs. path' vs. 'relative path' - if (substr($mask, 0, 2) === '//') { - $this->type = self::HOST; - - } elseif (substr($mask, 0, 1) === '/') { - $this->type = self::PATH; - - } else { - $this->type = self::RELATIVE; - } - - foreach ($metadata as $name => $meta) { - if (!is_array($meta)) { - $metadata[$name] = array(self::VALUE => $meta, 'fixity' => self::CONSTANT); - - } elseif (array_key_exists(self::VALUE, $meta)) { - $metadata[$name]['fixity'] = self::CONSTANT; - } - } - - // PARSE MASK - // or [ or ] or ?... - $parts = Strings::split($mask, '/<([^>#= ]+)(=[^># ]*)? *([^>#]*)(#?[^>\[\]]*)>|(\[!?|\]|\s*\?.*)/'); - - $this->xlat = array(); - $i = count($parts) - 1; - - // PARSE QUERY PART OF MASK - if (isset($parts[$i - 1]) && substr(ltrim($parts[$i - 1]), 0, 1) === '?') { - // name= - $matches = Strings::matchAll($parts[$i - 1], '/(?:([a-zA-Z0-9_.-]+)=)?<([^># ]+) *([^>#]*)(#?[^>]*)>/'); - - foreach ($matches as $match) { - list(, $param, $name, $pattern, $class) = $match; // $pattern is not used - - if ($class !== '') { - if (!isset(self::$styles[$class])) { - throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set."); - } - $meta = self::$styles[$class]; - - } elseif (isset(self::$styles['?' . $name])) { - $meta = self::$styles['?' . $name]; - - } else { - $meta = self::$styles['?#']; - } - - if (isset($metadata[$name])) { - $meta = $metadata[$name] + $meta; - } - - if (array_key_exists(self::VALUE, $meta)) { - $meta['fixity'] = self::OPTIONAL; - } - - unset($meta['pattern']); - $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]); - - $metadata[$name] = $meta; - if ($param !== '') { - $this->xlat[$name] = $param; - } - } - $i -= 6; - } - - // PARSE PATH PART OF MASK - $brackets = 0; // optional level - $re = ''; - $sequence = array(); - $autoOptional = array(0, 0); // strlen($re), count($sequence) - do { - array_unshift($sequence, $parts[$i]); - $re = preg_quote($parts[$i], '#') . $re; - if ($i === 0) { - break; - } - $i--; - - $part = $parts[$i]; // [ or ] - if ($part === '[' || $part === ']' || $part === '[!') { - $brackets += $part[0] === '[' ? -1 : 1; - if ($brackets < 0) { - throw new Nette\InvalidArgumentException("Unexpected '$part' in mask '$mask'."); - } - array_unshift($sequence, $part); - $re = ($part[0] === '[' ? '(?:' : ')?') . $re; - $i -= 5; - continue; - } - - $class = $parts[$i]; $i--; // validation class - $pattern = trim($parts[$i]); $i--; // validation condition (as regexp) - $default = $parts[$i]; $i--; // default value - $name = $parts[$i]; $i--; // parameter name - array_unshift($sequence, $name); - - if ($name[0] === '?') { // "foo" parameter - $re = '(?:' . preg_quote(substr($name, 1), '#') . '|' . $pattern . ')' . $re; - $sequence[1] = substr($name, 1) . $sequence[1]; - continue; - } - - // check name (limitation by regexp) - if (preg_match('#[^a-z0-9_-]#i', $name)) { - throw new Nette\InvalidArgumentException("Parameter name must be alphanumeric string due to limitations of PCRE, '$name' given."); - } - - // pattern, condition & metadata - if ($class !== '') { - if (!isset(self::$styles[$class])) { - throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set."); - } - $meta = self::$styles[$class]; - - } elseif (isset(self::$styles[$name])) { - $meta = self::$styles[$name]; - - } else { - $meta = self::$styles['#']; - } - - if (isset($metadata[$name])) { - $meta = $metadata[$name] + $meta; - } - - if ($pattern == '' && isset($meta[self::PATTERN])) { - $pattern = $meta[self::PATTERN]; - } - - if ($default !== '') { - $meta[self::VALUE] = (string) substr($default, 1); - $meta['fixity'] = self::PATH_OPTIONAL; - } - - $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]); - if (array_key_exists(self::VALUE, $meta)) { - if (isset($meta['filterTable2'][$meta[self::VALUE]])) { - $meta['defOut'] = $meta['filterTable2'][$meta[self::VALUE]]; - - } elseif (isset($meta[self::FILTER_OUT])) { - $meta['defOut'] = call_user_func($meta[self::FILTER_OUT], $meta[self::VALUE]); - - } else { - $meta['defOut'] = $meta[self::VALUE]; - } - } - $meta[self::PATTERN] = "#(?:$pattern)$#A" . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu'); - - // include in expression - $re = '(?P<' . str_replace('-', '___', $name) . '>(?U)' . $pattern . ')' . $re; // str_replace is dirty trick to enable '-' in parameter name - if ($brackets) { // is in brackets? - if (!isset($meta[self::VALUE])) { - $meta[self::VALUE] = $meta['defOut'] = NULL; - } - $meta['fixity'] = self::PATH_OPTIONAL; - - } elseif (isset($meta['fixity'])) { // auto-optional - $re = '(?:' . substr_replace($re, ')?', strlen($re) - $autoOptional[0], 0); - array_splice($sequence, count($sequence) - $autoOptional[1], 0, array(']', '')); - array_unshift($sequence, '[', ''); - $meta['fixity'] = self::PATH_OPTIONAL; - - } else { - $autoOptional = array(strlen($re), count($sequence)); - } - - $metadata[$name] = $meta; - } while (TRUE); - - if ($brackets) { - throw new Nette\InvalidArgumentException("Missing closing ']' in mask '$mask'."); - } - - $this->re = '#' . $re . '/?$#A' . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu'); - $this->metadata = $metadata; - $this->sequence = $sequence; - } - - - - /** - * Returns mask. - * @return string - */ - public function getMask() - { - return $this->mask; - } - - - - /** - * Returns default values. - * @return array - */ - public function getDefaults() - { - $defaults = array(); - foreach ($this->metadata as $name => $meta) { - if (isset($meta['fixity'])) { - $defaults[$name] = $meta[self::VALUE]; - } - } - return $defaults; - } - - - - /********************* Utilities ****************d*g**/ - - - - /** - * Proprietary cache aim. - * @return string|FALSE - */ - public function getTargetPresenter() - { - if ($this->flags & self::ONE_WAY) { - return FALSE; - } - - $m = $this->metadata; - $module = ''; - - if (isset($m[self::MODULE_KEY])) { - if (isset($m[self::MODULE_KEY]['fixity']) && $m[self::MODULE_KEY]['fixity'] === self::CONSTANT) { - $module = $m[self::MODULE_KEY][self::VALUE] . ':'; - } else { - return NULL; - } - } - - if (isset($m[self::PRESENTER_KEY]['fixity']) && $m[self::PRESENTER_KEY]['fixity'] === self::CONSTANT) { - return $module . $m[self::PRESENTER_KEY][self::VALUE]; - } - return NULL; - } - - - - /** - * Rename keys in array. - * @param array - * @param array - * @return array - */ - private static function renameKeys($arr, $xlat) - { - if (empty($xlat)) { - return $arr; - } - - $res = array(); - $occupied = array_flip($xlat); - foreach ($arr as $k => $v) { - if (isset($xlat[$k])) { - $res[$xlat[$k]] = $v; - - } elseif (!isset($occupied[$k])) { - $res[$k] = $v; - } - } - return $res; - } - - - - /********************* Inflectors ****************d*g**/ - - - - /** - * camelCaseAction name -> dash-separated. - * @param string - * @return string - */ - private static function action2path($s) - { - $s = preg_replace('#(.)(?=[A-Z])#', '$1-', $s); - $s = strtolower($s); - $s = rawurlencode($s); - return $s; - } - - - - /** - * dash-separated -> camelCaseAction name. - * @param string - * @return string - */ - private static function path2action($s) - { - $s = strtolower($s); - $s = preg_replace('#-(?=[a-z])#', ' ', $s); - $s = substr(ucwords('x' . $s), 1); - //$s = lcfirst(ucwords($s)); - $s = str_replace(' ', '', $s); - return $s; - } - - - - /** - * PascalCase:Presenter name -> dash-and-dot-separated. - * @param string - * @return string - */ - private static function presenter2path($s) - { - $s = strtr($s, ':', '.'); - $s = preg_replace('#([^.])(?=[A-Z])#', '$1-', $s); - $s = strtolower($s); - $s = rawurlencode($s); - return $s; - } - - - - /** - * dash-and-dot-separated -> PascalCase:Presenter name. - * @param string - * @return string - */ - private static function path2presenter($s) - { - $s = strtolower($s); - $s = preg_replace('#([.-])(?=[a-z])#', '$1 ', $s); - $s = ucwords($s); - $s = str_replace('. ', ':', $s); - $s = str_replace('- ', '', $s); - return $s; - } - - - - /********************* Route::$styles manipulator ****************d*g**/ - - - - /** - * Creates new style. - * @param string style name (#style, urlParameter, ?queryParameter) - * @param string optional parent style name - * @return void - */ - public static function addStyle($style, $parent = '#') - { - if (isset(self::$styles[$style])) { - throw new Nette\InvalidArgumentException("Style '$style' already exists."); - } - - if ($parent !== NULL) { - if (!isset(self::$styles[$parent])) { - throw new Nette\InvalidArgumentException("Parent style '$parent' doesn't exist."); - } - self::$styles[$style] = self::$styles[$parent]; - - } else { - self::$styles[$style] = array(); - } - } - - - - /** - * Changes style property value. - * @param string style name (#style, urlParameter, ?queryParameter) - * @param string property name (Route::PATTERN, Route::FILTER_IN, Route::FILTER_OUT, Route::FILTER_TABLE) - * @param mixed property value - * @return void - */ - public static function setStyleProperty($style, $key, $value) - { - if (!isset(self::$styles[$style])) { - throw new Nette\InvalidArgumentException("Style '$style' doesn't exist."); - } - self::$styles[$style][$key] = $value; - } - -} + array( // default style for path parameters + self::PATTERN => '[^/]+', + self::FILTER_IN => 'rawurldecode', + self::FILTER_OUT => array(__CLASS__, 'param2path'), + ), + '?#' => array( // default style for query parameters + ), + 'module' => array( + self::PATTERN => '[a-z][a-z0-9.-]*', + self::FILTER_IN => array(__CLASS__, 'path2presenter'), + self::FILTER_OUT => array(__CLASS__, 'presenter2path'), + ), + 'presenter' => array( + self::PATTERN => '[a-z][a-z0-9.-]*', + self::FILTER_IN => array(__CLASS__, 'path2presenter'), + self::FILTER_OUT => array(__CLASS__, 'presenter2path'), + ), + 'action' => array( + self::PATTERN => '[a-z][a-z0-9-]*', + self::FILTER_IN => array(__CLASS__, 'path2action'), + self::FILTER_OUT => array(__CLASS__, 'action2path'), + ), + '?module' => array( + ), + '?presenter' => array( + ), + '?action' => array( + ), + ); + + /** @var string */ + private $mask; + + /** @var array */ + private $sequence; + + /** @var string regular expression pattern */ + private $re; + + /** @var array of [value & fixity, filterIn, filterOut] */ + private $metadata = array(); + + /** @var array */ + private $xlat; + + /** @var int HOST, PATH, RELATIVE */ + private $type; + + /** @var int */ + private $flags; + + + + /** + * @param string URL mask, e.g. '//' + * @param array|string default values or metadata + * @param int flags + */ + public function __construct($mask, $metadata = array(), $flags = 0) + { + if (is_string($metadata)) { + $a = strrpos($metadata, ':'); + if (!$a) { + throw new Nette\InvalidArgumentException("Second argument must be array or string in format Presenter:action, '$metadata' given."); + } + $metadata = array( + self::PRESENTER_KEY => substr($metadata, 0, $a), + 'action' => $a === strlen($metadata) - 1 ? NULL : substr($metadata, $a + 1), + ); + } elseif ($metadata instanceof \Closure || $metadata instanceof Nette\Callback) { + $metadata = array( + self::PRESENTER_KEY => 'Nette:Micro', + 'callback' => $metadata, + ); + } + + $this->flags = $flags | static::$defaultFlags; + $this->setMask($mask, $metadata); + } + + + + /** + * Maps HTTP request to a Request object. + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + // combine with precedence: mask (params in URL-path), fixity, query, (post,) defaults + + // 1) URL MASK + $url = $httpRequest->getUrl(); + + if ($this->type === self::HOST) { + $path = '//' . $url->getHost() . $url->getPath(); + + } elseif ($this->type === self::RELATIVE) { + $basePath = $url->getBasePath(); + if (strncmp($url->getPath(), $basePath, strlen($basePath)) !== 0) { + return NULL; + } + $path = (string) substr($url->getPath(), strlen($basePath)); + + } else { + $path = $url->getPath(); + } + + if ($path !== '') { + $path = rtrim($path, '/') . '/'; + } + + if (!$matches = Strings::match($path, $this->re)) { + // stop, not matched + return NULL; + } + + // deletes numeric keys, restore '-' chars + $params = array(); + foreach ($matches as $k => $v) { + if (is_string($k) && $v !== '') { + $params[str_replace('___', '-', $k)] = $v; // trick + } + } + + + // 2) CONSTANT FIXITY + foreach ($this->metadata as $name => $meta) { + if (isset($params[$name])) { + //$params[$name] = $this->flags & self::CASE_SENSITIVE === 0 ? strtolower($params[$name]) : */$params[$name]; // strtolower damages UTF-8 + + } elseif (isset($meta['fixity']) && $meta['fixity'] !== self::OPTIONAL) { + $params[$name] = NULL; // cannot be overwriten in 3) and detected by isset() in 4) + } + } + + + // 3) QUERY + if ($this->xlat) { + $params += self::renameKeys($httpRequest->getQuery(), array_flip($this->xlat)); + } else { + $params += $httpRequest->getQuery(); + } + + + // 4) APPLY FILTERS & FIXITY + foreach ($this->metadata as $name => $meta) { + if (isset($params[$name])) { + if (!is_scalar($params[$name])) { + + } elseif (isset($meta[self::FILTER_TABLE][$params[$name]])) { // applies filterTable only to scalar parameters + $params[$name] = $meta[self::FILTER_TABLE][$params[$name]]; + + } elseif (isset($meta[self::FILTER_TABLE]) && !empty($meta[self::FILTER_STRICT])) { + return NULL; // rejected by filterTable + + } elseif (isset($meta[self::FILTER_IN])) { // applies filterIn only to scalar parameters + $params[$name] = call_user_func($meta[self::FILTER_IN], (string) $params[$name]); + if ($params[$name] === NULL && !isset($meta['fixity'])) { + return NULL; // rejected by filter + } + } + + } elseif (isset($meta['fixity'])) { + $params[$name] = $meta[self::VALUE]; + } + } + + + // 5) BUILD Request + if (!isset($params[self::PRESENTER_KEY])) { + throw new Nette\InvalidStateException('Missing presenter in route definition.'); + } + if (isset($this->metadata[self::MODULE_KEY])) { + if (!isset($params[self::MODULE_KEY])) { + throw new Nette\InvalidStateException('Missing module in route definition.'); + } + $presenter = $params[self::MODULE_KEY] . ':' . $params[self::PRESENTER_KEY]; + unset($params[self::MODULE_KEY], $params[self::PRESENTER_KEY]); + + } else { + $presenter = $params[self::PRESENTER_KEY]; + unset($params[self::PRESENTER_KEY]); + } + + return new Application\Request( + $presenter, + $httpRequest->getMethod(), + $params, + $httpRequest->getPost(), + $httpRequest->getFiles(), + array(Application\Request::SECURED => $httpRequest->isSecured()) + ); + } + + + + /** + * Constructs absolute URL from Request object. + * @return string|NULL + */ + public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) + { + if ($this->flags & self::ONE_WAY) { + return NULL; + } + + $params = $appRequest->getParameters(); + $metadata = $this->metadata; + + $presenter = $appRequest->getPresenterName(); + $params[self::PRESENTER_KEY] = $presenter; + + if (isset($metadata[self::MODULE_KEY])) { // try split into module and [submodule:]presenter parts + $module = $metadata[self::MODULE_KEY]; + if (isset($module['fixity']) && strncasecmp($presenter, $module[self::VALUE] . ':', strlen($module[self::VALUE]) + 1) === 0) { + $a = strlen($module[self::VALUE]); + } else { + $a = strrpos($presenter, ':'); + } + if ($a === FALSE) { + $params[self::MODULE_KEY] = ''; + } else { + $params[self::MODULE_KEY] = substr($presenter, 0, $a); + $params[self::PRESENTER_KEY] = substr($presenter, $a + 1); + } + } + + foreach ($metadata as $name => $meta) { + if (!isset($params[$name])) { + continue; // retains NULL values + } + + if (isset($meta['fixity'])) { + if ($params[$name] === FALSE) { + $params[$name] = '0'; + } + if (is_scalar($params[$name]) ? strcasecmp($params[$name], $meta[self::VALUE]) === 0 + : $params[$name] === $meta[self::VALUE] + ) { // remove default values; NULL values are retain + unset($params[$name]); + continue; + + } elseif ($meta['fixity'] === self::CONSTANT) { + return NULL; // missing or wrong parameter '$name' + } + } + + if (!is_scalar($params[$name])) { + + } elseif (isset($meta['filterTable2'][$params[$name]])) { + $params[$name] = $meta['filterTable2'][$params[$name]]; + + } elseif (isset($meta['filterTable2']) && !empty($meta[self::FILTER_STRICT])) { + return NULL; + + } elseif (isset($meta[self::FILTER_OUT])) { + $params[$name] = call_user_func($meta[self::FILTER_OUT], $params[$name]); + } + + if (isset($meta[self::PATTERN]) && !preg_match($meta[self::PATTERN], rawurldecode($params[$name]))) { + return NULL; // pattern not match + } + } + + // compositing path + $sequence = $this->sequence; + $brackets = array(); + $required = NULL; // NULL for auto-optional + $url = ''; + $i = count($sequence) - 1; + do { + $url = $sequence[$i] . $url; + if ($i === 0) { + break; + } + $i--; + + $name = $sequence[$i]; $i--; // parameter name + + if ($name === ']') { // opening optional part + $brackets[] = $url; + + } elseif ($name[0] === '[') { // closing optional part + $tmp = array_pop($brackets); + if ($required < count($brackets) + 1) { // is this level optional? + if ($name !== '[!') { // and not "required"-optional + $url = $tmp; + } + } else { + $required = count($brackets); + } + + } elseif ($name[0] === '?') { // "foo" parameter + continue; + + } elseif (isset($params[$name]) && $params[$name] != '') { // intentionally == + $required = count($brackets); // make this level required + $url = $params[$name] . $url; + unset($params[$name]); + + } elseif (isset($metadata[$name]['fixity'])) { // has default value? + if ($required === NULL && !$brackets) { // auto-optional + $url = ''; + } else { + $url = $metadata[$name]['defOut'] . $url; + } + + } else { + return NULL; // missing parameter '$name' + } + } while (TRUE); + + + // build query string + if ($this->xlat) { + $params = self::renameKeys($params, $this->xlat); + } + + $sep = ini_get('arg_separator.input'); + $query = http_build_query($params, '', $sep ? $sep[0] : '&'); + if ($query != '') { // intentionally == + $url .= '?' . $query; + } + + // absolutize path + if ($this->type === self::RELATIVE) { + $url = '//' . $refUrl->getAuthority() . $refUrl->getBasePath() . $url; + + } elseif ($this->type === self::PATH) { + $url = '//' . $refUrl->getAuthority() . $url; + } + + if (strpos($url, '//', 2) !== FALSE) { + return NULL; // TODO: implement counterpart in match() ? + } + + $url = ($this->flags & self::SECURED ? 'https:' : 'http:') . $url; + + return $url; + } + + + + /** + * Parse mask and array of default values; initializes object. + * @param string + * @param array + * @return void + */ + private function setMask($mask, array $metadata) + { + $this->mask = $mask; + + // detect '//host/path' vs. '/abs. path' vs. 'relative path' + if (substr($mask, 0, 2) === '//') { + $this->type = self::HOST; + + } elseif (substr($mask, 0, 1) === '/') { + $this->type = self::PATH; + + } else { + $this->type = self::RELATIVE; + } + + foreach ($metadata as $name => $meta) { + if (!is_array($meta)) { + $metadata[$name] = array(self::VALUE => $meta, 'fixity' => self::CONSTANT); + + } elseif (array_key_exists(self::VALUE, $meta)) { + $metadata[$name]['fixity'] = self::CONSTANT; + } + } + + // PARSE MASK + // or [ or ] or ?... + $parts = Strings::split($mask, '/<([^>#= ]+)(=[^># ]*)? *([^>#]*)(#?[^>\[\]]*)>|(\[!?|\]|\s*\?.*)/'); + + $this->xlat = array(); + $i = count($parts) - 1; + + // PARSE QUERY PART OF MASK + if (isset($parts[$i - 1]) && substr(ltrim($parts[$i - 1]), 0, 1) === '?') { + // name= + $matches = Strings::matchAll($parts[$i - 1], '/(?:([a-zA-Z0-9_.-]+)=)?<([^># ]+) *([^>#]*)(#?[^>]*)>/'); + + foreach ($matches as $match) { + list(, $param, $name, $pattern, $class) = $match; // $pattern is not used + + if ($class !== '') { + if (!isset(static::$styles[$class])) { + throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set."); + } + $meta = static::$styles[$class]; + + } elseif (isset(static::$styles['?' . $name])) { + $meta = static::$styles['?' . $name]; + + } else { + $meta = static::$styles['?#']; + } + + if (isset($metadata[$name])) { + $meta = $metadata[$name] + $meta; + } + + if (array_key_exists(self::VALUE, $meta)) { + $meta['fixity'] = self::OPTIONAL; + } + + unset($meta['pattern']); + $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]); + + $metadata[$name] = $meta; + if ($param !== '') { + $this->xlat[$name] = $param; + } + } + $i -= 6; + } + + // PARSE PATH PART OF MASK + $brackets = 0; // optional level + $re = ''; + $sequence = array(); + $autoOptional = TRUE; + do { + array_unshift($sequence, $parts[$i]); + $re = preg_quote($parts[$i], '#') . $re; + if ($i === 0) { + break; + } + $i--; + + $part = $parts[$i]; // [ or ] + if ($part === '[' || $part === ']' || $part === '[!') { + $brackets += $part[0] === '[' ? -1 : 1; + if ($brackets < 0) { + throw new Nette\InvalidArgumentException("Unexpected '$part' in mask '$mask'."); + } + array_unshift($sequence, $part); + $re = ($part[0] === '[' ? '(?:' : ')?') . $re; + $i -= 5; + continue; + } + + $class = $parts[$i]; $i--; // validation class + $pattern = trim($parts[$i]); $i--; // validation condition (as regexp) + $default = $parts[$i]; $i--; // default value + $name = $parts[$i]; $i--; // parameter name + array_unshift($sequence, $name); + + if ($name[0] === '?') { // "foo" parameter + $re = '(?:' . preg_quote(substr($name, 1), '#') . '|' . $pattern . ')' . $re; + $sequence[1] = substr($name, 1) . $sequence[1]; + continue; + } + + // check name (limitation by regexp) + if (preg_match('#[^a-z0-9_-]#i', $name)) { + throw new Nette\InvalidArgumentException("Parameter name must be alphanumeric string due to limitations of PCRE, '$name' given."); + } + + // pattern, condition & metadata + if ($class !== '') { + if (!isset(static::$styles[$class])) { + throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set."); + } + $meta = static::$styles[$class]; + + } elseif (isset(static::$styles[$name])) { + $meta = static::$styles[$name]; + + } else { + $meta = static::$styles['#']; + } + + if (isset($metadata[$name])) { + $meta = $metadata[$name] + $meta; + } + + if ($pattern == '' && isset($meta[self::PATTERN])) { + $pattern = $meta[self::PATTERN]; + } + + if ($default !== '') { + $meta[self::VALUE] = (string) substr($default, 1); + $meta['fixity'] = self::PATH_OPTIONAL; + } + + $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]); + if (array_key_exists(self::VALUE, $meta)) { + if (isset($meta['filterTable2'][$meta[self::VALUE]])) { + $meta['defOut'] = $meta['filterTable2'][$meta[self::VALUE]]; + + } elseif (isset($meta[self::FILTER_OUT])) { + $meta['defOut'] = call_user_func($meta[self::FILTER_OUT], $meta[self::VALUE]); + + } else { + $meta['defOut'] = $meta[self::VALUE]; + } + } + $meta[self::PATTERN] = "#(?:$pattern)$#A" . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu'); + + // include in expression + $re = '(?P<' . str_replace('-', '___', $name) . '>(?U)' . $pattern . ')' . $re; // str_replace is dirty trick to enable '-' in parameter name + if ($brackets) { // is in brackets? + if (!isset($meta[self::VALUE])) { + $meta[self::VALUE] = $meta['defOut'] = NULL; + } + $meta['fixity'] = self::PATH_OPTIONAL; + + } elseif (!$autoOptional) { + unset($meta['fixity']); + + } elseif (isset($meta['fixity'])) { // auto-optional + $re = '(?:' . $re . ')?'; + $meta['fixity'] = self::PATH_OPTIONAL; + + } else { + $autoOptional = FALSE; + } + + $metadata[$name] = $meta; + } while (TRUE); + + if ($brackets) { + throw new Nette\InvalidArgumentException("Missing closing ']' in mask '$mask'."); + } + + $this->re = '#' . $re . '/?$#A' . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu'); + $this->metadata = $metadata; + $this->sequence = $sequence; + } + + + + /** + * Returns mask. + * @return string + */ + public function getMask() + { + return $this->mask; + } + + + + /** + * Returns default values. + * @return array + */ + public function getDefaults() + { + $defaults = array(); + foreach ($this->metadata as $name => $meta) { + if (isset($meta['fixity'])) { + $defaults[$name] = $meta[self::VALUE]; + } + } + return $defaults; + } + + + + /** + * Returns flags. + * @return int + */ + public function getFlags() + { + return $this->flags; + } + + + + /********************* Utilities ****************d*g**/ + + + + /** + * Proprietary cache aim. + * @return string|FALSE + */ + public function getTargetPresenter() + { + if ($this->flags & self::ONE_WAY) { + return FALSE; + } + + $m = $this->metadata; + $module = ''; + + if (isset($m[self::MODULE_KEY])) { + if (isset($m[self::MODULE_KEY]['fixity']) && $m[self::MODULE_KEY]['fixity'] === self::CONSTANT) { + $module = $m[self::MODULE_KEY][self::VALUE] . ':'; + } else { + return NULL; + } + } + + if (isset($m[self::PRESENTER_KEY]['fixity']) && $m[self::PRESENTER_KEY]['fixity'] === self::CONSTANT) { + return $module . $m[self::PRESENTER_KEY][self::VALUE]; + } + return NULL; + } + + + + /** + * Rename keys in array. + * @param array + * @param array + * @return array + */ + private static function renameKeys($arr, $xlat) + { + if (empty($xlat)) { + return $arr; + } + + $res = array(); + $occupied = array_flip($xlat); + foreach ($arr as $k => $v) { + if (isset($xlat[$k])) { + $res[$xlat[$k]] = $v; + + } elseif (!isset($occupied[$k])) { + $res[$k] = $v; + } + } + return $res; + } + + + + /********************* Inflectors ****************d*g**/ + + + + /** + * camelCaseAction name -> dash-separated. + * @param string + * @return string + */ + private static function action2path($s) + { + $s = preg_replace('#(.)(?=[A-Z])#', '$1-', $s); + $s = strtolower($s); + $s = rawurlencode($s); + return $s; + } + + + + /** + * dash-separated -> camelCaseAction name. + * @param string + * @return string + */ + private static function path2action($s) + { + $s = strtolower($s); + $s = preg_replace('#-(?=[a-z])#', ' ', $s); + $s = substr(ucwords('x' . $s), 1); + //$s = lcfirst(ucwords($s)); + $s = str_replace(' ', '', $s); + return $s; + } + + + + /** + * PascalCase:Presenter name -> dash-and-dot-separated. + * @param string + * @return string + */ + private static function presenter2path($s) + { + $s = strtr($s, ':', '.'); + $s = preg_replace('#([^.])(?=[A-Z])#', '$1-', $s); + $s = strtolower($s); + $s = rawurlencode($s); + return $s; + } + + + + /** + * dash-and-dot-separated -> PascalCase:Presenter name. + * @param string + * @return string + */ + private static function path2presenter($s) + { + $s = strtolower($s); + $s = preg_replace('#([.-])(?=[a-z])#', '$1 ', $s); + $s = ucwords($s); + $s = str_replace('. ', ':', $s); + $s = str_replace('- ', '', $s); + return $s; + } + + + + /** + * Url encode. + * @param string + * @return string + */ + private static function param2path($s) + { + return str_replace('%2F', '/', rawurlencode($s)); + } + + + + /********************* Route::$styles manipulator ****************d*g**/ + + + + /** + * Creates new style. + * @param string style name (#style, urlParameter, ?queryParameter) + * @param string optional parent style name + * @return void + */ + public static function addStyle($style, $parent = '#') + { + if (isset(static::$styles[$style])) { + throw new Nette\InvalidArgumentException("Style '$style' already exists."); + } + + if ($parent !== NULL) { + if (!isset(static::$styles[$parent])) { + throw new Nette\InvalidArgumentException("Parent style '$parent' doesn't exist."); + } + static::$styles[$style] = static::$styles[$parent]; + + } else { + static::$styles[$style] = array(); + } + } + + + + /** + * Changes style property value. + * @param string style name (#style, urlParameter, ?queryParameter) + * @param string property name (Route::PATTERN, Route::FILTER_IN, Route::FILTER_OUT, Route::FILTER_TABLE) + * @param mixed property value + * @return void + */ + public static function setStyleProperty($style, $key, $value) + { + if (!isset(static::$styles[$style])) { + throw new Nette\InvalidArgumentException("Style '$style' doesn't exist."); + } + static::$styles[$style][$key] = $value; + } + +} diff --git a/libs/Nette/Application/Routers/RouteList.php b/libs/Nette/Application/Routers/RouteList.php index e93cf06..ab4cfd2 100644 --- a/libs/Nette/Application/Routers/RouteList.php +++ b/libs/Nette/Application/Routers/RouteList.php @@ -1,135 +1,143 @@ -module = $module ? $module . ':' : ''; - } - - - - /** - * Maps HTTP request to a Request object. - * @param Nette\Http\IRequest - * @return Nette\Application\Request|NULL - */ - public function match(Nette\Http\IRequest $httpRequest) - { - foreach ($this as $route) { - $appRequest = $route->match($httpRequest); - if ($appRequest !== NULL) { - $appRequest->setPresenterName($this->module . $appRequest->getPresenterName()); - return $appRequest; - } - } - return NULL; - } - - - - /** - * Constructs absolute URL from Request object. - * @param Nette\Application\Request - * @param Nette\Http\Url - * @return string|NULL - */ - public function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl) - { - if ($this->cachedRoutes === NULL) { - $routes = array(); - $routes['*'] = array(); - - foreach ($this as $route) { - $presenter = $route instanceof Route ? $route->getTargetPresenter() : NULL; - - if ($presenter === FALSE) { - continue; - } - - if (is_string($presenter)) { - $presenter = strtolower($presenter); - if (!isset($routes[$presenter])) { - $routes[$presenter] = $routes['*']; - } - $routes[$presenter][] = $route; - - } else { - foreach ($routes as $id => $foo) { - $routes[$id][] = $route; - } - } - } - - $this->cachedRoutes = $routes; - } - - if ($this->module) { - if (strncasecmp($tmp = $appRequest->getPresenterName(), $this->module, strlen($this->module)) === 0) { - $appRequest = clone $appRequest; - $appRequest->setPresenterName(substr($tmp, strlen($this->module))); - } else { - return NULL; - } - } - - $presenter = strtolower($appRequest->getPresenterName()); - if (!isset($this->cachedRoutes[$presenter])) { - $presenter = '*'; - } - - foreach ($this->cachedRoutes[$presenter] as $route) { - $url = $route->constructUrl($appRequest, $refUrl); - if ($url !== NULL) { - return $url; - } - } - - return NULL; - } - - - - /** - * Adds the router. - * @param mixed - * @param Nette\Application\IRouter - * @return void - */ - public function offsetSet($index, $route) - { - if (!$route instanceof Nette\Application\IRouter) { - throw new Nette\InvalidArgumentException("Argument must be IRouter descendant."); - } - parent::offsetSet($index, $route); - } - -} +module = $module ? $module . ':' : ''; + } + + + + /** + * Maps HTTP request to a Request object. + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + foreach ($this as $route) { + $appRequest = $route->match($httpRequest); + if ($appRequest !== NULL) { + $appRequest->setPresenterName($this->module . $appRequest->getPresenterName()); + return $appRequest; + } + } + return NULL; + } + + + + /** + * Constructs absolute URL from Request object. + * @return string|NULL + */ + public function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl) + { + if ($this->cachedRoutes === NULL) { + $routes = array(); + $routes['*'] = array(); + + foreach ($this as $route) { + $presenter = $route instanceof Route ? $route->getTargetPresenter() : NULL; + + if ($presenter === FALSE) { + continue; + } + + if (is_string($presenter)) { + $presenter = strtolower($presenter); + if (!isset($routes[$presenter])) { + $routes[$presenter] = $routes['*']; + } + $routes[$presenter][] = $route; + + } else { + foreach ($routes as $id => $foo) { + $routes[$id][] = $route; + } + } + } + + $this->cachedRoutes = $routes; + } + + if ($this->module) { + if (strncasecmp($tmp = $appRequest->getPresenterName(), $this->module, strlen($this->module)) === 0) { + $appRequest = clone $appRequest; + $appRequest->setPresenterName(substr($tmp, strlen($this->module))); + } else { + return NULL; + } + } + + $presenter = strtolower($appRequest->getPresenterName()); + if (!isset($this->cachedRoutes[$presenter])) { + $presenter = '*'; + } + + foreach ($this->cachedRoutes[$presenter] as $route) { + $url = $route->constructUrl($appRequest, $refUrl); + if ($url !== NULL) { + return $url; + } + } + + return NULL; + } + + + + /** + * Adds the router. + * @param mixed + * @param Nette\Application\IRouter + * @return void + */ + public function offsetSet($index, $route) + { + if (!$route instanceof Nette\Application\IRouter) { + throw new Nette\InvalidArgumentException("Argument must be IRouter descendant."); + } + parent::offsetSet($index, $route); + } + + + + /** + * @return string + */ + public function getModule() + { + return $this->module; + } + +} diff --git a/libs/Nette/Application/Routers/SimpleRouter.php b/libs/Nette/Application/Routers/SimpleRouter.php index 0d99eb1..65b16fa 100644 --- a/libs/Nette/Application/Routers/SimpleRouter.php +++ b/libs/Nette/Application/Routers/SimpleRouter.php @@ -1,146 +1,160 @@ - substr($defaults, 0, $a), - 'action' => $a === strlen($defaults) - 1 ? Application\UI\Presenter::DEFAULT_ACTION : substr($defaults, $a + 1), - ); - } - - if (isset($defaults[self::MODULE_KEY])) { - $this->module = $defaults[self::MODULE_KEY] . ':'; - unset($defaults[self::MODULE_KEY]); - } - - $this->defaults = $defaults; - $this->flags = $flags; - } - - - - /** - * Maps HTTP request to a Request object. - * @param Nette\Http\IRequest - * @return Nette\Application\Request|NULL - */ - public function match(Nette\Http\IRequest $httpRequest) - { - if ($httpRequest->getUrl()->getPathInfo() !== '') { - return NULL; - } - // combine with precedence: get, (post,) defaults - $params = $httpRequest->getQuery(); - $params += $this->defaults; - - if (!isset($params[self::PRESENTER_KEY])) { - throw new Nette\InvalidStateException('Missing presenter.'); - } - - $presenter = $this->module . $params[self::PRESENTER_KEY]; - unset($params[self::PRESENTER_KEY]); - - return new Application\Request( - $presenter, - $httpRequest->getMethod(), - $params, - $httpRequest->getPost(), - $httpRequest->getFiles(), - array(Application\Request::SECURED => $httpRequest->isSecured()) - ); - } - - - - /** - * Constructs absolute URL from Request object. - * @param Nette\Application\Request - * @param Nette\Http\Url - * @return string|NULL - */ - public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) - { - $params = $appRequest->getParams(); - - // presenter name - $presenter = $appRequest->getPresenterName(); - if (strncasecmp($presenter, $this->module, strlen($this->module)) === 0) { - $params[self::PRESENTER_KEY] = substr($presenter, strlen($this->module)); - } else { - return NULL; - } - - // remove default values; NULL values are retain - foreach ($this->defaults as $key => $value) { - if (isset($params[$key]) && $params[$key] == $value) { // intentionally == - unset($params[$key]); - } - } - - $url = ($this->flags & self::SECURED ? 'https://' : 'http://') . $refUrl->getAuthority() . $refUrl->getPath(); - $sep = ini_get('arg_separator.input'); - $query = http_build_query($params, '', $sep ? $sep[0] : '&'); - if ($query != '') { // intentionally == - $url .= '?' . $query; - } - return $url; - } - - - - /** - * Returns default values. - * @return array - */ - public function getDefaults() - { - return $this->defaults; - } - -} + substr($defaults, 0, $a), + 'action' => $a === strlen($defaults) - 1 ? Application\UI\Presenter::DEFAULT_ACTION : substr($defaults, $a + 1), + ); + } + + if (isset($defaults[self::MODULE_KEY])) { + $this->module = $defaults[self::MODULE_KEY] . ':'; + unset($defaults[self::MODULE_KEY]); + } + + $this->defaults = $defaults; + $this->flags = $flags; + } + + + + /** + * Maps HTTP request to a Request object. + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + if ($httpRequest->getUrl()->getPathInfo() !== '') { + return NULL; + } + // combine with precedence: get, (post,) defaults + $params = $httpRequest->getQuery(); + $params += $this->defaults; + + if (!isset($params[self::PRESENTER_KEY])) { + throw new Nette\InvalidStateException('Missing presenter.'); + } + + $presenter = $this->module . $params[self::PRESENTER_KEY]; + unset($params[self::PRESENTER_KEY]); + + return new Application\Request( + $presenter, + $httpRequest->getMethod(), + $params, + $httpRequest->getPost(), + $httpRequest->getFiles(), + array(Application\Request::SECURED => $httpRequest->isSecured()) + ); + } + + + + /** + * Constructs absolute URL from Request object. + * @return string|NULL + */ + public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) + { + if ($this->flags & self::ONE_WAY) { + return NULL; + } + $params = $appRequest->getParameters(); + + // presenter name + $presenter = $appRequest->getPresenterName(); + if (strncasecmp($presenter, $this->module, strlen($this->module)) === 0) { + $params[self::PRESENTER_KEY] = substr($presenter, strlen($this->module)); + } else { + return NULL; + } + + // remove default values; NULL values are retain + foreach ($this->defaults as $key => $value) { + if (isset($params[$key]) && $params[$key] == $value) { // intentionally == + unset($params[$key]); + } + } + + $url = ($this->flags & self::SECURED ? 'https://' : 'http://') . $refUrl->getAuthority() . $refUrl->getPath(); + $sep = ini_get('arg_separator.input'); + $query = http_build_query($params, '', $sep ? $sep[0] : '&'); + if ($query != '') { // intentionally == + $url .= '?' . $query; + } + return $url; + } + + + + /** + * Returns default values. + * @return array + */ + public function getDefaults() + { + return $this->defaults; + } + + + + /** + * Returns flags. + * @return int + */ + public function getFlags() + { + return $this->flags; + } + +} diff --git a/libs/Nette/Application/UI/BadSignalException.php b/libs/Nette/Application/UI/BadSignalException.php index 825509a..4401bb5 100644 --- a/libs/Nette/Application/UI/BadSignalException.php +++ b/libs/Nette/Application/UI/BadSignalException.php @@ -1,28 +1,28 @@ -template === NULL) { - $value = $this->createTemplate(); - if (!$value instanceof Nette\Templating\ITemplate && $value !== NULL) { - $class2 = get_class($value); $class = get_class($this); - throw new Nette\UnexpectedValueException("Object returned by $class::createTemplate() must be instance of Nette\\Templating\\ITemplate, '$class2' given."); - } - $this->template = $value; - } - return $this->template; - } - - - - /** - * @return Nette\Templating\ITemplate - */ - protected function createTemplate() - { - $template = new Nette\Templating\FileTemplate; - $presenter = $this->getPresenter(FALSE); - $template->onPrepareFilters[] = callback($this, 'templatePrepareFilters'); - - // default parameters - $template->control = $this; - $template->presenter = $presenter; - if ($presenter instanceof Presenter) { - $template->setCacheStorage($presenter->getContext()->templateCacheStorage); - $template->user = $presenter->getUser(); - $template->netteHttpResponse = $presenter->getHttpResponse(); - $template->netteCacheStorage = $presenter->getContext()->cacheStorage; - $template->baseUri = $template->baseUrl = rtrim($presenter->getHttpRequest()->getUrl()->getBaseUrl(), '/'); - $template->basePath = preg_replace('#https?://[^/]+#A', '', $template->baseUrl); - - // flash message - if ($presenter->hasFlashSession()) { - $id = $this->getParamId('flash'); - $template->flashes = $presenter->getFlashSession()->$id; - } - } - if (!isset($template->flashes) || !is_array($template->flashes)) { - $template->flashes = array(); - } - - // default helpers - $template->registerHelper('escape', 'Nette\Templating\DefaultHelpers::escapeHtml'); - $template->registerHelper('escapeUrl', 'rawurlencode'); - $template->registerHelper('stripTags', 'strip_tags'); - $template->registerHelper('nl2br', 'nl2br'); - $template->registerHelper('substr', 'iconv_substr'); - $template->registerHelper('repeat', 'str_repeat'); - $template->registerHelper('replaceRE', 'Nette\Utils\Strings::replace'); - $template->registerHelper('implode', 'implode'); - $template->registerHelper('number', 'number_format'); - $template->registerHelperLoader('Nette\Templating\DefaultHelpers::loader'); - - return $template; - } - - - - /** - * Descendant can override this method to customize template compile-time filters. - * @param Nette\Templating\Template - * @return void - */ - public function templatePrepareFilters($template) - { - // default filters - $template->registerFilter(new Nette\Latte\Engine); - } - - - - /** - * Returns widget component specified by name. - * @param string - * @return Nette\ComponentModel\IComponent - */ - public function getWidget($name) - { - return $this->getComponent($name); - } - - - - /** - * Saves the message to template, that can be displayed after redirect. - * @param string - * @param string - * @return stdClass - */ - public function flashMessage($message, $type = 'info') - { - $id = $this->getParamId('flash'); - $messages = $this->getPresenter()->getFlashSession()->$id; - $messages[] = $flash = (object) array( - 'message' => $message, - 'type' => $type, - ); - $this->getTemplate()->flashes = $messages; - $this->getPresenter()->getFlashSession()->$id = $messages; - return $flash; - } - - - - /********************* rendering ****************d*g**/ - - - - /** - * Forces control or its snippet to repaint. - * @param string - * @return void - */ - public function invalidateControl($snippet = NULL) - { - $this->invalidSnippets[$snippet] = TRUE; - } - - - - /** - * Allows control or its snippet to not repaint. - * @param string - * @return void - */ - public function validateControl($snippet = NULL) - { - if ($snippet === NULL) { - $this->invalidSnippets = array(); - - } else { - unset($this->invalidSnippets[$snippet]); - } - } - - - - /** - * Is required to repaint the control or its snippet? - * @param string snippet name - * @return bool - */ - public function isControlInvalid($snippet = NULL) - { - if ($snippet === NULL) { - if (count($this->invalidSnippets) > 0) { - return TRUE; - - } else { - foreach ($this->getComponents() as $component) { - if ($component instanceof IRenderable && $component->isControlInvalid()) { - // $this->invalidSnippets['__child'] = TRUE; // as cache - return TRUE; - } - } - return FALSE; - } - - } else { - return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]); - } - } - - - - /** - * Returns snippet HTML ID. - * @param string snippet name - * @return string - */ - public function getSnippetId($name = NULL) - { - // HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]* - return 'snippet-' . $this->getUniqueId() . '-' . $name; - } - -} +template === NULL) { + $value = $this->createTemplate(); + if (!$value instanceof Nette\Templating\ITemplate && $value !== NULL) { + $class2 = get_class($value); $class = get_class($this); + throw new Nette\UnexpectedValueException("Object returned by $class::createTemplate() must be instance of Nette\\Templating\\ITemplate, '$class2' given."); + } + $this->template = $value; + } + return $this->template; + } + + + + /** + * @param string|NULL + * @return Nette\Templating\ITemplate + */ + protected function createTemplate($class = NULL) + { + $template = $class ? new $class : new Nette\Templating\FileTemplate; + $presenter = $this->getPresenter(FALSE); + $template->onPrepareFilters[] = $this->templatePrepareFilters; + $template->registerHelperLoader('Nette\Templating\Helpers::loader'); + + // default parameters + $template->control = $template->_control = $this; + $template->presenter = $template->_presenter = $presenter; + if ($presenter instanceof Presenter) { + $template->setCacheStorage($presenter->getContext()->nette->templateCacheStorage); + $template->user = $presenter->getUser(); + $template->netteHttpResponse = $presenter->getHttpResponse(); + $template->netteCacheStorage = $presenter->getContext()->getByType('Nette\Caching\IStorage'); + $template->baseUri = $template->baseUrl = rtrim($presenter->getHttpRequest()->getUrl()->getBaseUrl(), '/'); + $template->basePath = preg_replace('#https?://[^/]+#A', '', $template->baseUrl); + + // flash message + if ($presenter->hasFlashSession()) { + $id = $this->getParameterId('flash'); + $template->flashes = $presenter->getFlashSession()->$id; + } + } + if (!isset($template->flashes) || !is_array($template->flashes)) { + $template->flashes = array(); + } + + return $template; + } + + + + /** + * Descendant can override this method to customize template compile-time filters. + * @param Nette\Templating\Template + * @return void + */ + public function templatePrepareFilters($template) + { + $template->registerFilter($this->getPresenter()->getContext()->nette->createLatte()); + } + + + + /** + * Returns widget component specified by name. + * @param string + * @return Nette\ComponentModel\IComponent + */ + public function getWidget($name) + { + trigger_error(__METHOD__ . '() is deprecated, use getComponent() instead.', E_USER_WARNING); + return $this->getComponent($name); + } + + + + /** + * Saves the message to template, that can be displayed after redirect. + * @param string + * @param string + * @return \stdClass + */ + public function flashMessage($message, $type = 'info') + { + $id = $this->getParameterId('flash'); + $messages = $this->getPresenter()->getFlashSession()->$id; + $messages[] = $flash = (object) array( + 'message' => $message, + 'type' => $type, + ); + $this->getTemplate()->flashes = $messages; + $this->getPresenter()->getFlashSession()->$id = $messages; + return $flash; + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Forces control or its snippet to repaint. + * @param string + * @return void + */ + public function invalidateControl($snippet = NULL) + { + $this->invalidSnippets[$snippet] = TRUE; + } + + + + /** + * Allows control or its snippet to not repaint. + * @param string + * @return void + */ + public function validateControl($snippet = NULL) + { + if ($snippet === NULL) { + $this->invalidSnippets = array(); + + } else { + unset($this->invalidSnippets[$snippet]); + } + } + + + + /** + * Is required to repaint the control or its snippet? + * @param string snippet name + * @return bool + */ + public function isControlInvalid($snippet = NULL) + { + if ($snippet === NULL) { + if (count($this->invalidSnippets) > 0) { + return TRUE; + + } else { + $queue = array($this); + do { + foreach (array_shift($queue)->getComponents() as $component) { + if ($component instanceof IRenderable) { + if ($component->isControlInvalid()) { + // $this->invalidSnippets['__child'] = TRUE; // as cache + return TRUE; + } + + } elseif ($component instanceof Nette\ComponentModel\IContainer) { + $queue[] = $component; + } + } + } while ($queue); + + return FALSE; + } + + } else { + return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]); + } + } + + + + /** + * Returns snippet HTML ID. + * @param string snippet name + * @return string + */ + public function getSnippetId($name = NULL) + { + // HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]* + return 'snippet-' . $this->getUniqueId() . '-' . $name; + } + +} diff --git a/libs/Nette/Application/UI/Form.php b/libs/Nette/Application/UI/Form.php index b5bc2cb..6db389a 100644 --- a/libs/Nette/Application/UI/Form.php +++ b/libs/Nette/Application/UI/Form.php @@ -1,143 +1,147 @@ -monitor('Nette\Application\UI\Presenter'); - if ($parent !== NULL) { - $parent->addComponent($this, $name); - } - } - - - - /** - * Returns the presenter where this component belongs to. - * @param bool throw exception if presenter doesn't exist? - * @return Presenter|NULL - */ - public function getPresenter($need = TRUE) - { - return $this->lookup('Nette\Application\UI\Presenter', $need); - } - - - - /** - * This method will be called when the component (or component's parent) - * becomes attached to a monitored object. Do not call this method yourself. - * @param Nette\Application\IComponent - * @return void - */ - protected function attached($presenter) - { - if ($presenter instanceof Presenter) { - $name = $this->lookupPath('Nette\Application\UI\Presenter'); - - if (!isset($this->getElementPrototype()->id)) { - $this->getElementPrototype()->id = 'frm-' . $name; - } - - $this->setAction(new Link( - $presenter, - $name . self::NAME_SEPARATOR . 'submit!', - array() - )); - - // fill-in the form with HTTP data - if ($this->isSubmitted()) { - foreach ($this->getControls() as $control) { - $control->loadHttpData(); - } - } - } - parent::attached($presenter); - } - - - - /** - * Tells if the form is anchored. - * @return bool - */ - public function isAnchored() - { - return (bool) $this->getPresenter(FALSE); - } - - - - /** - * Internal: receives submitted HTTP data. - * @return array - */ - protected function receiveHttpData() - { - $presenter = $this->getPresenter(); - if (!$presenter->isSignalReceiver($this, 'submit')) { - return; - } - - $isPost = $this->getMethod() === self::POST; - $request = $presenter->getRequest(); - if ($request->isMethod('forward') || $request->isMethod('post') !== $isPost) { - return; - } - - if ($isPost) { - return Nette\Utils\Arrays::mergeTree($request->getPost(), $request->getFiles()); - } else { - return $request->getParams(); - } - } - - - - /********************* interface ISignalReceiver ****************d*g**/ - - - - /** - * This method is called by presenter. - * @param string - * @return void - */ - public function signalReceived($signal) - { - if ($signal === 'submit') { - $this->fireEvents(); - } else { - $class = get_class($this); - throw new BadSignalException("Missing handler for signal '$signal' in $class."); - } - } - -} +monitor('Nette\Application\UI\Presenter'); + if ($parent !== NULL) { + $parent->addComponent($this, $name); + } + } + + + + /** + * Returns the presenter where this component belongs to. + * @param bool throw exception if presenter doesn't exist? + * @return Presenter|NULL + */ + public function getPresenter($need = TRUE) + { + return $this->lookup('Nette\Application\UI\Presenter', $need); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param Nette\ComponentModel\IComponent + * @return void + */ + protected function attached($presenter) + { + if ($presenter instanceof Presenter) { + $name = $this->lookupPath('Nette\Application\UI\Presenter'); + + if (!isset($this->getElementPrototype()->id)) { + $this->getElementPrototype()->id = 'frm-' . $name; + } + + $this->setAction(new Link( + $presenter, + $name . self::NAME_SEPARATOR . 'submit!', + array() + )); + + // fill-in the form with HTTP data + if ($this->isSubmitted()) { + foreach ($this->getControls() as $control) { + if (!$control->isDisabled()) { + $control->loadHttpData(); + } + } + } + } + parent::attached($presenter); + } + + + + /** + * Tells if the form is anchored. + * @return bool + */ + public function isAnchored() + { + return (bool) $this->getPresenter(FALSE); + } + + + + /** + * Internal: receives submitted HTTP data. + * @return array + */ + protected function receiveHttpData() + { + $presenter = $this->getPresenter(); + if (!$presenter->isSignalReceiver($this, 'submit')) { + return; + } + + $isPost = $this->getMethod() === self::POST; + $request = $presenter->getRequest(); + if ($request->isMethod('forward') || $request->isMethod('post') !== $isPost) { + return; + } + + if ($isPost) { + return Nette\Utils\Arrays::mergeTree($request->getPost(), $request->getFiles()); + } else { + return $request->getParameters(); + } + } + + + + /********************* interface ISignalReceiver ****************d*g**/ + + + + /** + * This method is called by presenter. + * @param string + * @return void + */ + public function signalReceived($signal) + { + if ($signal === 'submit') { + if (!$this->getPresenter()->getRequest()->hasFlag(Nette\Application\Request::RESTORED)) { + $this->fireEvents(); + } + } else { + $class = get_class($this); + throw new BadSignalException("Missing handler for signal '$signal' in $class."); + } + } + +} diff --git a/libs/Nette/Application/UI/IPartiallyRenderable.php b/libs/Nette/Application/UI/IPartiallyRenderable.php deleted file mode 100644 index 0ff1e46..0000000 --- a/libs/Nette/Application/UI/IPartiallyRenderable.php +++ /dev/null @@ -1,40 +0,0 @@ -previous = $previous; + parent::__construct($message, $code); + } else { + parent::__construct($message, $code, $previous); + } + } + */ +} diff --git a/libs/Nette/Application/UI/Link.php b/libs/Nette/Application/UI/Link.php index 3bcce79..4178865 100644 --- a/libs/Nette/Application/UI/Link.php +++ b/libs/Nette/Application/UI/Link.php @@ -1,114 +1,114 @@ -component = $component; - $this->destination = $destination; - $this->params = $params; - } - - - - /** - * Returns link destination. - * @return string - */ - public function getDestination() - { - return $this->destination; - } - - - - /** - * Changes link parameter. - * @param string - * @param mixed - * @return Link provides a fluent interface - */ - public function setParam($key, $value) - { - $this->params[$key] = $value; - return $this; - } - - - - /** - * Returns link parameter. - * @param string - * @return mixed - */ - public function getParam($key) - { - return isset($this->params[$key]) ? $this->params[$key] : NULL; - } - - - - /** - * Returns link parameters. - * @return array - */ - public function getParams() - { - return $this->params; - } - - - - /** - * Converts link to URL. - * @return string - */ - public function __toString() - { - try { - return $this->component->link($this->destination, $this->params); - - } catch (\Exception $e) { - Nette\Diagnostics\Debugger::toStringException($e); - } - } - -} +component = $component; + $this->destination = $destination; + $this->params = $params; + } + + + + /** + * Returns link destination. + * @return string + */ + public function getDestination() + { + return $this->destination; + } + + + + /** + * Changes link parameter. + * @param string + * @param mixed + * @return Link provides a fluent interface + */ + public function setParameter($key, $value) + { + $this->params[$key] = $value; + return $this; + } + + + + /** + * Returns link parameter. + * @param string + * @return mixed + */ + public function getParameter($key) + { + return isset($this->params[$key]) ? $this->params[$key] : NULL; + } + + + + /** + * Returns link parameters. + * @return array + */ + public function getParameters() + { + return $this->params; + } + + + + /** + * Converts link to URL. + * @return string + */ + public function __toString() + { + try { + return $this->component->link($this->destination, $this->params); + + } catch (\Exception $e) { + trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR); + } + } + +} diff --git a/libs/Nette/Application/UI/Multiplier.php b/libs/Nette/Application/UI/Multiplier.php new file mode 100644 index 0000000..f9d8bd3 --- /dev/null +++ b/libs/Nette/Application/UI/Multiplier.php @@ -0,0 +1,42 @@ +factory = new Nette\Callback($factory); + } + + + + protected function createComponent($name) + { + return $this->factory->invoke($name, $this); + } + +} diff --git a/libs/Nette/Application/UI/Presenter.php b/libs/Nette/Application/UI/Presenter.php index e7cf3d1..9dd410b 100644 --- a/libs/Nette/Application/UI/Presenter.php +++ b/libs/Nette/Application/UI/Presenter.php @@ -1,1320 +1,1443 @@ -request; - } - - - - /** - * Returns self. - * @return Presenter - */ - final public function getPresenter($need = TRUE) - { - return $this; - } - - - - /** - * Returns a name that uniquely identifies component. - * @return string - */ - final public function getUniqueId() - { - return ''; - } - - - - /********************* interface IPresenter ****************d*g**/ - - - - /** - * @param Nette\Application\Request - * @return Nette\Application\IResponse - */ - public function run(Application\Request $request) - { - try { - // STARTUP - $this->request = $request; - $this->payload = (object) NULL; - $this->setParent($this->getParent(), $request->getPresenterName()); - - $this->initGlobalParams(); - $this->startup(); - if (!$this->startupCheck) { - $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName(); - throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup()."); - } - // calls $this->action() - $this->tryCall($this->formatActionMethod($this->getAction()), $this->params); - - if ($this->autoCanonicalize) { - $this->canonicalize(); - } - if ($this->getHttpRequest()->isMethod('head')) { - $this->terminate(); - } - - // SIGNAL HANDLING - // calls $this->handle() - $this->processSignal(); - - // RENDERING VIEW - $this->beforeRender(); - // calls $this->render() - $this->tryCall($this->formatRenderMethod($this->getView()), $this->params); - $this->afterRender(); - - // save component tree persistent state - $this->saveGlobalState(); - if ($this->isAjax()) { - $this->payload->state = $this->getGlobalState(); - } - - // finish template rendering - $this->sendTemplate(); - - } catch (Application\AbortException $e) { - // continue with shutting down - if ($this->isAjax()) try { - $hasPayload = (array) $this->payload; unset($hasPayload['state']); - if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) { // snippets - TODO - $this->response->send($this->getHttpRequest(), $this->getHttpResponse()); - $this->sendPayload(); - - } elseif (!$this->response && $hasPayload) { // back compatibility for use terminate() instead of sendPayload() - $this->sendPayload(); - } - } catch (Application\AbortException $e) { } - - if ($this->hasFlashSession()) { - $this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds': '+ 3 seconds'); - } - - // SHUTDOWN - $this->onShutdown($this, $this->response); - $this->shutdown($this->response); - - return $this->response; - } - } - - - - /** - * @return void - */ - protected function startup() - { - $this->startupCheck = TRUE; - } - - - - /** - * Common render method. - * @return void - */ - protected function beforeRender() - { - } - - - - /** - * Common render method. - * @return void - */ - protected function afterRender() - { - } - - - - /** - * @param Nette\Application\IResponse optional catched exception - * @return void - */ - protected function shutdown($response) - { - } - - - - /********************* signal handling ****************d*g**/ - - - - /** - * @return void - * @throws BadSignalException - */ - public function processSignal() - { - if ($this->signal === NULL) { - return; - } - - $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE); - if ($component === NULL) { - throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found."); - - } elseif (!$component instanceof ISignalReceiver) { - throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor."); - } - - $component->signalReceived($this->signal); - $this->signal = NULL; - } - - - - /** - * Returns pair signal receiver and name. - * @return array|NULL - */ - final public function getSignal() - { - return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal); - } - - - - /** - * Checks if the signal receiver is the given one. - * @param mixed component or its id - * @param string signal name (optional) - * @return bool - */ - final public function isSignalReceiver($component, $signal = NULL) - { - if ($component instanceof Nette\ComponentModel\Component) { - $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE); - } - - if ($this->signal === NULL) { - return FALSE; - - } elseif ($signal === TRUE) { - return $component === '' - || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0; - - } elseif ($signal === NULL) { - return $this->signalReceiver === $component; - - } else { - return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0; - } - } - - - - /********************* rendering ****************d*g**/ - - - - /** - * Returns current action name. - * @return string - */ - final public function getAction($fullyQualified = FALSE) - { - return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action; - } - - - - /** - * Changes current action. Only alphanumeric characters are allowed. - * @param string - * @return void - */ - public function changeAction($action) - { - if (Nette\Utils\Strings::match($action, "#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*$#")) { - $this->action = $action; - $this->view = $action; - - } else { - throw new Application\BadRequestException("Action name '$action' is not alphanumeric string."); - } - } - - - - /** - * Returns current view. - * @return string - */ - final public function getView() - { - return $this->view; - } - - - - /** - * Changes current view. Any name is allowed. - * @param string - * @return Presenter provides a fluent interface - */ - public function setView($view) - { - $this->view = (string) $view; - return $this; - } - - - - /** - * Returns current layout name. - * @return string|FALSE - */ - final public function getLayout() - { - return $this->layout; - } - - - - /** - * Changes or disables layout. - * @param string|FALSE - * @return Presenter provides a fluent interface - */ - public function setLayout($layout) - { - $this->layout = $layout === FALSE ? FALSE : (string) $layout; - return $this; - } - - - - /** - * @return void - * @throws Nette\Application\BadRequestException if no template found - * @throws Nette\Application\AbortException - */ - public function sendTemplate() - { - $template = $this->getTemplate(); - if (!$template) { - return; - } - - if ($template instanceof Nette\Templating\IFileTemplate && !$template->getFile()) { // content template - $files = $this->formatTemplateFiles(); - foreach ($files as $file) { - if (is_file($file)) { - $template->setFile($file); - break; - } - } - - if (!$template->getFile()) { - $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files)); - $file = strtr($file, '/', DIRECTORY_SEPARATOR); - throw new Application\BadRequestException("Page not found. Missing template '$file'."); - } - } - - if ($this->layout !== FALSE) { // layout template - $files = $this->formatLayoutTemplateFiles(); - foreach ($files as $file) { - if (is_file($file)) { - $template->layout = $file; - $template->_extends = $file; - break; - } - } - - if (empty($template->layout) && $this->layout !== NULL) { - $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files)); - $file = strtr($file, '/', DIRECTORY_SEPARATOR); - throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'."); - } - } - - $this->sendResponse(new Responses\TextResponse($template)); - } - - - - /** - * Formats layout template file names. - * @return array - */ - public function formatLayoutTemplateFiles() - { - $name = $this->getName(); - $presenter = substr($name, strrpos(':' . $name, ':')); - $layout = $this->layout ? $this->layout : 'layout'; - $dir = dirname(dirname($this->getReflection()->getFileName())); - $list = array( - "$dir/templates/$presenter/@$layout.latte", - "$dir/templates/$presenter.@$layout.latte", - "$dir/templates/$presenter/@$layout.phtml", - "$dir/templates/$presenter.@$layout.phtml", - ); - do { - $list[] = "$dir/templates/@$layout.latte"; - $list[] = "$dir/templates/@$layout.phtml"; - $dir = dirname($dir); - } while ($dir && ($name = substr($name, 0, strrpos($name, ':')))); - return $list; - } - - - - /** - * Formats view template file names. - * @return array - */ - public function formatTemplateFiles() - { - $name = $this->getName(); - $presenter = substr($name, strrpos(':' . $name, ':')); - $dir = dirname(dirname($this->getReflection()->getFileName())); - return array( - "$dir/templates/$presenter/$this->view.latte", - "$dir/templates/$presenter.$this->view.latte", - "$dir/templates/$presenter/$this->view.phtml", - "$dir/templates/$presenter.$this->view.phtml", - ); - } - - - - /** - * Formats action method name. - * @param string - * @return string - */ - protected static function formatActionMethod($action) - { - return 'action' . $action; - } - - - - /** - * Formats render view method name. - * @param string - * @return string - */ - protected static function formatRenderMethod($view) - { - return 'render' . $view; - } - - - - /********************* partial AJAX rendering ****************d*g**/ - - - - /** - * @return stdClass - */ - final public function getPayload() - { - return $this->payload; - } - - - - /** - * Is AJAX request? - * @return bool - */ - public function isAjax() - { - if ($this->ajaxMode === NULL) { - $this->ajaxMode = $this->getHttpRequest()->isAjax(); - } - return $this->ajaxMode; - } - - - - /** - * Sends AJAX payload to the output. - * @return void - * @throws Nette\Application\AbortException - */ - public function sendPayload() - { - $this->sendResponse(new Responses\JsonResponse($this->payload)); - } - - - - /********************* navigation & flow ****************d*g**/ - - - - /** - * Sends response and terminates presenter. - * @param Nette\Application\IResponse - * @return void - * @throws Nette\Application\AbortException - */ - public function sendResponse(Application\IResponse $response) - { - $this->response = $response; - $this->terminate(); - } - - - - /** - * Correctly terminates presenter. - * @return void - * @throws Nette\Application\AbortException - */ - public function terminate() - { - if (func_num_args() !== 0) { - trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING); - $this->sendResponse(func_get_arg(0)); - } - throw new Application\AbortException(); - } - - - - /** - * Forward to another presenter or action. - * @param string|Request - * @param array|mixed - * @return void - * @throws Nette\Application\AbortException - */ - public function forward($destination, $args = array()) - { - if ($destination instanceof Application\Request) { - $this->sendResponse(new Responses\ForwardResponse($destination)); - - } elseif (!is_array($args)) { - $args = func_get_args(); - array_shift($args); - } - - $this->createRequest($this, $destination, $args, 'forward'); - $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest)); - } - - - - /** - * Redirect to another URL and ends presenter execution. - * @param string - * @param int HTTP error code - * @return void - * @throws Nette\Application\AbortException - */ - public function redirectUrl($url, $code = NULL) - { - if ($this->isAjax()) { - $this->payload->redirect = (string) $url; - $this->sendPayload(); - - } elseif (!$code) { - $code = $this->getHttpRequest()->isMethod('post') - ? Http\IResponse::S303_POST_GET - : Http\IResponse::S302_FOUND; - } - $this->sendResponse(new Responses\RedirectResponse($url, $code)); - } - - /** @deprecated */ - function redirectUri($url, $code = NULL) - { - trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING); - $this->redirectUrl($url, $code); - } - - - - /** - * Link to myself. - * @return string - */ - public function backlink() - { - return $this->getAction(TRUE); - } - - - - /** - * Returns the last created Request. - * @return Nette\Application\Request - */ - public function getLastCreatedRequest() - { - return $this->lastCreatedRequest; - } - - - - /** - * Returns the last created Request flag. - * @param string - * @return bool - */ - public function getLastCreatedRequestFlag($flag) - { - return !empty($this->lastCreatedRequestFlag[$flag]); - } - - - - /** - * Conditional redirect to canonicalized URI. - * @return void - * @throws Nette\Application\AbortException - */ - public function canonicalize() - { - if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) { - $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->params, 'redirectX'); - if ($url !== NULL && !$this->getHttpRequest()->getUrl()->isEqual($url)) { - $this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY)); - } - } - } - - - - /** - * Attempts to cache the sent entity by its last modification date. - * @param string|int|DateTime last modified time - * @param string strong entity tag validator - * @param mixed optional expiration time - * @return void - * @throws Nette\Application\AbortException - * @deprecated - */ - public function lastModified($lastModified, $etag = NULL, $expire = NULL) - { - if ($expire !== NULL) { - $this->getHttpResponse()->setExpiration($expire); - } - - if (!$this->getHttpContext()->isModified($lastModified, $etag)) { - $this->terminate(); - } - } - - - - /** - * Request/URL factory. - * @param PresenterComponent base - * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" - * @param array array of arguments - * @param string forward|redirect|link - * @return string URL - * @throws InvalidLinkException - * @internal - */ - final protected function createRequest($component, $destination, array $args, $mode) - { - // note: createRequest supposes that saveState(), run() & tryCall() behaviour is final - - // cached services for better performance - static $presenterFactory, $router, $refUrl; - if ($presenterFactory === NULL) { - $presenterFactory = $this->getApplication()->getPresenterFactory(); - $router = $this->getApplication()->getRouter(); - $refUrl = new Http\Url($this->getHttpRequest()->getUrl()); - $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath()); - } - - $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL; - - // PARSE DESTINATION - // 1) fragment - $a = strpos($destination, '#'); - if ($a === FALSE) { - $fragment = ''; - } else { - $fragment = substr($destination, $a); - $destination = substr($destination, 0, $a); - } - - // 2) ?query syntax - $a = strpos($destination, '?'); - if ($a !== FALSE) { - parse_str(substr($destination, $a + 1), $args); // requires disabled magic quotes - $destination = substr($destination, 0, $a); - } - - // 3) URL scheme - $a = strpos($destination, '//'); - if ($a === FALSE) { - $scheme = FALSE; - } else { - $scheme = substr($destination, 0, $a); - $destination = substr($destination, $a + 2); - } - - // 4) signal or empty - if (!$component instanceof Presenter || substr($destination, -1) === '!') { - $signal = rtrim($destination, '!'); - $a = strrpos($signal, ':'); - if ($a !== FALSE) { - $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-')); - $signal = (string) substr($signal, $a + 1); - } - if ($signal == NULL) { // intentionally == - throw new InvalidLinkException("Signal must be non-empty string."); - } - $destination = 'this'; - } - - if ($destination == NULL) { // intentionally == - throw new InvalidLinkException("Destination must be non-empty string."); - } - - // 5) presenter: action - $current = FALSE; - $a = strrpos($destination, ':'); - if ($a === FALSE) { - $action = $destination === 'this' ? $this->action : $destination; - $presenter = $this->getName(); - $presenterClass = get_class($this); - - } else { - $action = (string) substr($destination, $a + 1); - if ($destination[0] === ':') { // absolute - if ($a < 2) { - throw new InvalidLinkException("Missing presenter name in '$destination'."); - } - $presenter = substr($destination, 1, $a - 1); - - } else { // relative - $presenter = $this->getName(); - $b = strrpos($presenter, ':'); - if ($b === FALSE) { // no module - $presenter = substr($destination, 0, $a); - } else { // with module - $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a); - } - } - try { - $presenterClass = $presenterFactory->getPresenterClass($presenter); - } catch (Application\InvalidPresenterException $e) { - throw new InvalidLinkException($e->getMessage(), NULL, $e); - } - } - - // PROCESS SIGNAL ARGUMENTS - if (isset($signal)) { // $component must be IStatePersistent - $reflection = new PresenterComponentReflection(get_class($component)); - if ($signal === 'this') { // means "no signal" - $signal = ''; - if (array_key_exists(0, $args)) { - throw new InvalidLinkException("Unable to pass parameters to 'this!' signal."); - } - - } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) { // TODO: AppForm exception - // counterpart of signalReceived() & tryCall() - $method = $component->formatSignalMethod($signal); - if (!$reflection->hasCallableMethod($method)) { - throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()"); - } - if ($args) { // convert indexed parameters to named - self::argsToParams(get_class($component), $method, $args); - } - } - - // counterpart of IStatePersistent - if ($args && array_intersect_key($args, $reflection->getPersistentParams())) { - $component->saveState($args); - } - - if ($args && $component !== $this) { - $prefix = $component->getUniqueId() . self::NAME_SEPARATOR; - foreach ($args as $key => $val) { - unset($args[$key]); - $args[$prefix . $key] = $val; - } - } - } - - // PROCESS ARGUMENTS - if (is_subclass_of($presenterClass, __CLASS__)) { - if ($action === '') { - $action = self::DEFAULT_ACTION; - } - - $current = ($action === '*' || $action === $this->action) && $presenterClass === get_class($this); // TODO - - $reflection = new PresenterComponentReflection($presenterClass); - if ($args || $destination === 'this') { - // counterpart of run() & tryCall() - $method = $presenterClass::formatActionMethod($action); - if (!$reflection->hasCallableMethod($method)) { - $method = $presenterClass::formatRenderMethod($action); - if (!$reflection->hasCallableMethod($method)) { - $method = NULL; - } - } - - // convert indexed parameters to named - if ($method === NULL) { - if (array_key_exists(0, $args)) { - throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method."); - } - - } elseif ($destination === 'this') { - self::argsToParams($presenterClass, $method, $args, $this->params); - - } else { - self::argsToParams($presenterClass, $method, $args); - } - } - - // counterpart of IStatePersistent - if ($args && array_intersect_key($args, $reflection->getPersistentParams())) { - $this->saveState($args, $reflection); - } - - $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass); - if ($current && $args) { - $tmp = $globalState + $this->params; - foreach ($args as $key => $val) { - if ((string) $val !== (isset($tmp[$key]) ? (string) $tmp[$key] : '')) { - $current = FALSE; - break; - } - } - } - $args += $globalState; - } - - // ADD ACTION & SIGNAL & FLASH - $args[self::ACTION_KEY] = $action; - if (!empty($signal)) { - $args[self::SIGNAL_KEY] = $component->getParamId($signal); - $current = $current && $args[self::SIGNAL_KEY] === $this->getParam(self::SIGNAL_KEY); - } - if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) { - $args[self::FLASH_KEY] = $this->getParam(self::FLASH_KEY); - } - - $this->lastCreatedRequest = new Application\Request( - $presenter, - Application\Request::FORWARD, - $args, - array(), - array() - ); - $this->lastCreatedRequestFlag = array('current' => $current); - - if ($mode === 'forward') { - return; - } - - // CONSTRUCT URL - $url = $router->constructUrl($this->lastCreatedRequest, $refUrl); - if ($url === NULL) { - unset($args[self::ACTION_KEY]); - $params = urldecode(http_build_query($args, NULL, ', ')); - throw new InvalidLinkException("No route for $presenter:$action($params)"); - } - - // make URL relative if possible - if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) { - $hostUrl = $refUrl->getHostUrl(); - if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) { - $url = substr($url, strlen($hostUrl)); - } - } - - return $url . $fragment; - } - - - - /** - * Converts list of arguments to named parameters. - * @param string class name - * @param string method name - * @param array arguments - * @param array supplemental arguments - * @return void - * @throws InvalidLinkException - */ - private static function argsToParams($class, $method, & $args, $supplemental = array()) - { - static $cache; - $params = & $cache[strtolower($class . ':' . $method)]; - if ($params === NULL) { - $params = Reflection\Method::from($class, $method)->getDefaultParameters(); - } - $i = 0; - foreach ($params as $name => $def) { - if (array_key_exists($i, $args)) { - $args[$name] = $args[$i]; - unset($args[$i]); - $i++; - - } elseif (array_key_exists($name, $args)) { - // continue with process - - } elseif (array_key_exists($name, $supplemental)) { - $args[$name] = $supplemental[$name]; - - } else { - continue; - } - - if ($def === NULL) { - if ((string) $args[$name] === '') { - $args[$name] = NULL; // value transmit is unnecessary - } - } else { - settype($args[$name], gettype($def)); - if ($args[$name] === $def) { - $args[$name] = NULL; - } - } - } - - if (array_key_exists($i, $args)) { - $method = Reflection\Method::from($class, $method)->getName(); - throw new InvalidLinkException("Passed more parameters than method $class::$method() expects."); - } - } - - - - /** - * Invalid link handler. Descendant can override this method to change default behaviour. - * @param InvalidLinkException - * @return string - * @throws InvalidLinkException - */ - protected function handleInvalidLink($e) - { - if (self::$invalidLinkMode === self::INVALID_LINK_SILENT) { - return '#'; - - } elseif (self::$invalidLinkMode === self::INVALID_LINK_WARNING) { - return 'error: ' . $e->getMessage(); - - } else { // self::INVALID_LINK_EXCEPTION - throw $e; - } - } - - - - /********************* interface IStatePersistent ****************d*g**/ - - - - /** - * Returns array of persistent components. - * This default implementation detects components by class-level annotation @persistent(cmp1, cmp2). - * @return array - */ - public static function getPersistentComponents() - { - return (array) Reflection\ClassType::from(get_called_class()) - ->getAnnotation('persistent'); - } - - - - /** - * Saves state information for all subcomponents to $this->globalState. - * @return array - */ - private function getGlobalState($forClass = NULL) - { - $sinces = & $this->globalStateSinces; - - if ($this->globalState === NULL) { - $state = array(); - foreach ($this->globalParams as $id => $params) { - $prefix = $id . self::NAME_SEPARATOR; - foreach ($params as $key => $val) { - $state[$prefix . $key] = $val; - } - } - $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL); - - if ($sinces === NULL) { - $sinces = array(); - foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) { - $sinces[$nm] = $meta['since']; - } - } - - $components = $this->getReflection()->getPersistentComponents(); - $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent'); - - foreach ($iterator as $name => $component) { - if ($iterator->getDepth() === 0) { - // counts with Nette\Application\RecursiveIteratorIterator::SELF_FIRST - $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE; // FALSE = nonpersistent - } - $prefix = $component->getUniqueId() . self::NAME_SEPARATOR; - $params = array(); - $component->saveState($params); - foreach ($params as $key => $val) { - $state[$prefix . $key] = $val; - $sinces[$prefix . $key] = $since; - } - } - - } else { - $state = $this->globalState; - } - - if ($forClass !== NULL) { - $since = NULL; - foreach ($state as $key => $foo) { - if (!isset($sinces[$key])) { - $x = strpos($key, self::NAME_SEPARATOR); - $x = $x === FALSE ? $key : substr($key, 0, $x); - $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE; - } - if ($since !== $sinces[$key]) { - $since = $sinces[$key]; - $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since); - } - if (!$ok) { - unset($state[$key]); - } - } - } - - return $state; - } - - - - /** - * Permanently saves state information for all subcomponents to $this->globalState. - * @return void - */ - protected function saveGlobalState() - { - // load lazy components - foreach ($this->globalParams as $id => $foo) { - $this->getComponent($id, FALSE); - } - - $this->globalParams = array(); - $this->globalState = $this->getGlobalState(); - } - - - - /** - * Initializes $this->globalParams, $this->signal & $this->signalReceiver, $this->action, $this->view. Called by run(). - * @return void - * @throws Nette\Application\BadRequestException if action name is not valid - */ - private function initGlobalParams() - { - // init $this->globalParams - $this->globalParams = array(); - $selfParams = array(); - - $params = $this->request->getParams(); - if ($this->isAjax()) { - $params = $this->request->getPost() + $params; - } - - foreach ($params as $key => $value) { - $a = strlen($key) > 2 ? strrpos($key, self::NAME_SEPARATOR, -2) : FALSE; - if ($a === FALSE) { - $selfParams[$key] = $value; - } else { - $this->globalParams[substr($key, 0, $a)][substr($key, $a + 1)] = $value; - } - } - - // init & validate $this->action & $this->view - $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION); - - // init $this->signalReceiver and key 'signal' in appropriate params array - $this->signalReceiver = $this->getUniqueId(); - if (!empty($selfParams[self::SIGNAL_KEY])) { - $param = $selfParams[self::SIGNAL_KEY]; - $pos = strrpos($param, '-'); - if ($pos) { - $this->signalReceiver = substr($param, 0, $pos); - $this->signal = substr($param, $pos + 1); - } else { - $this->signalReceiver = $this->getUniqueId(); - $this->signal = $param; - } - if ($this->signal == NULL) { // intentionally == - $this->signal = NULL; - } - } - - $this->loadState($selfParams); - } - - - - /** - * Pops parameters for specified component. - * @param string component id - * @return array - */ - final public function popGlobalParams($id) - { - if (isset($this->globalParams[$id])) { - $res = $this->globalParams[$id]; - unset($this->globalParams[$id]); - return $res; - - } else { - return array(); - } - } - - - - /********************* flash session ****************d*g**/ - - - - /** - * Checks if a flash session namespace exists. - * @return bool - */ - public function hasFlashSession() - { - return !empty($this->params[self::FLASH_KEY]) - && $this->getSession()->hasNamespace('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); - } - - - - /** - * Returns session namespace provided to pass temporary data between redirects. - * @return Nette\Http\SessionNamespace - */ - public function getFlashSession() - { - if (empty($this->params[self::FLASH_KEY])) { - $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4); - } - return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); - } - - - - /********************* services ****************d*g**/ - - - - /** - * Gets the context. - * @return Presenter provides a fluent interface - */ - public function setContext(Nette\DI\IContainer $context) - { - $this->context = $context; - return $this; - } - - - - /** - * Gets the context. - * @return Nette\DI\IContainer - */ - final public function getContext() - { - return $this->context; - } - - - - /** - * @return Nette\Http\Request - */ - protected function getHttpRequest() - { - return $this->context->httpRequest; - } - - - - /** - * @return Nette\Http\Response - */ - protected function getHttpResponse() - { - return $this->context->httpResponse; - } - - - - /** - * @return Nette\Http\Context - */ - protected function getHttpContext() - { - return $this->context->httpContext; - } - - - - /** - * @return Nette\Application\Application - */ - public function getApplication() - { - return $this->context->application; - } - - - - /** - * @return Nette\Http\Session - */ - public function getSession($namespace = NULL) - { - $handler = $this->context->session; - return $namespace === NULL ? $handler : $handler->getNamespace($namespace); - } - - - - /** - * @return Nette\Http\User - */ - public function getUser() - { - return $this->context->user; - } - -} +context = $context; + if ($context && $this->invalidLinkMode === NULL) { + $this->invalidLinkMode = $context->parameters['productionMode'] ? self::INVALID_LINK_SILENT : self::INVALID_LINK_WARNING; + } + } + + + + /** + * @return Nette\Application\Request + */ + final public function getRequest() + { + return $this->request; + } + + + + /** + * Returns self. + * @return Presenter + */ + final public function getPresenter($need = TRUE) + { + return $this; + } + + + + /** + * Returns a name that uniquely identifies component. + * @return string + */ + final public function getUniqueId() + { + return ''; + } + + + + /********************* interface IPresenter ****************d*g**/ + + + + /** + * @return Nette\Application\IResponse + */ + public function run(Application\Request $request) + { + try { + // STARTUP + $this->request = $request; + $this->payload = (object) NULL; + $this->setParent($this->getParent(), $request->getPresenterName()); + + $this->initGlobalParameters(); + $this->checkRequirements($this->getReflection()); + $this->startup(); + if (!$this->startupCheck) { + $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName(); + throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup()."); + } + // calls $this->action() + $this->tryCall($this->formatActionMethod($this->getAction()), $this->params); + + if ($this->autoCanonicalize) { + $this->canonicalize(); + } + if ($this->getHttpRequest()->isMethod('head')) { + $this->terminate(); + } + + // SIGNAL HANDLING + // calls $this->handle() + $this->processSignal(); + + // RENDERING VIEW + $this->beforeRender(); + // calls $this->render() + $this->tryCall($this->formatRenderMethod($this->getView()), $this->params); + $this->afterRender(); + + // save component tree persistent state + $this->saveGlobalState(); + if ($this->isAjax()) { + $this->payload->state = $this->getGlobalState(); + } + + // finish template rendering + $this->sendTemplate(); + + } catch (Application\AbortException $e) { + // continue with shutting down + if ($this->isAjax()) try { + $hasPayload = (array) $this->payload; unset($hasPayload['state']); + if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) { // snippets - TODO + $this->snippetMode = TRUE; + $this->response->send($this->getHttpRequest(), $this->getHttpResponse()); + $this->sendPayload(); + + } elseif (!$this->response && $hasPayload) { // back compatibility for use terminate() instead of sendPayload() + $this->sendPayload(); + } + } catch (Application\AbortException $e) { } + + if ($this->hasFlashSession()) { + $this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds': '+ 3 seconds'); + } + + // SHUTDOWN + $this->onShutdown($this, $this->response); + $this->shutdown($this->response); + + return $this->response; + } + } + + + + /** + * @return void + */ + protected function startup() + { + $this->startupCheck = TRUE; + } + + + + /** + * Common render method. + * @return void + */ + protected function beforeRender() + { + } + + + + /** + * Common render method. + * @return void + */ + protected function afterRender() + { + } + + + + /** + * @param Nette\Application\IResponse + * @return void + */ + protected function shutdown($response) + { + } + + + + /** + * Checks authorization. + * @return void + */ + public function checkRequirements($element) + { + $user = (array) $element->getAnnotation('User'); + if (in_array('loggedIn', $user) && !$this->getUser()->isLoggedIn()) { + throw new Application\ForbiddenRequestException; + } + } + + + + /********************* signal handling ****************d*g**/ + + + + /** + * @return void + * @throws BadSignalException + */ + public function processSignal() + { + if ($this->signal === NULL) { + return; + } + + try { + $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE); + } catch (Nette\InvalidArgumentException $e) {} + + if (isset($e) || $component === NULL) { + throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found."); + + } elseif (!$component instanceof ISignalReceiver) { + throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor."); + } + + $component->signalReceived($this->signal); + $this->signal = NULL; + } + + + + /** + * Returns pair signal receiver and name. + * @return array|NULL + */ + final public function getSignal() + { + return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal); + } + + + + /** + * Checks if the signal receiver is the given one. + * @param mixed component or its id + * @param string signal name (optional) + * @return bool + */ + final public function isSignalReceiver($component, $signal = NULL) + { + if ($component instanceof Nette\ComponentModel\Component) { + $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE); + } + + if ($this->signal === NULL) { + return FALSE; + + } elseif ($signal === TRUE) { + return $component === '' + || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0; + + } elseif ($signal === NULL) { + return $this->signalReceiver === $component; + + } else { + return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0; + } + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Returns current action name. + * @return string + */ + final public function getAction($fullyQualified = FALSE) + { + return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action; + } + + + + /** + * Changes current action. Only alphanumeric characters are allowed. + * @param string + * @return void + */ + public function changeAction($action) + { + if (is_string($action) && Nette\Utils\Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*$#')) { + $this->action = $action; + $this->view = $action; + + } else { + $this->error('Action name is not alphanumeric string.'); + } + } + + + + /** + * Returns current view. + * @return string + */ + final public function getView() + { + return $this->view; + } + + + + /** + * Changes current view. Any name is allowed. + * @param string + * @return Presenter provides a fluent interface + */ + public function setView($view) + { + $this->view = (string) $view; + return $this; + } + + + + /** + * Returns current layout name. + * @return string|FALSE + */ + final public function getLayout() + { + return $this->layout; + } + + + + /** + * Changes or disables layout. + * @param string|FALSE + * @return Presenter provides a fluent interface + */ + public function setLayout($layout) + { + $this->layout = $layout === FALSE ? FALSE : (string) $layout; + return $this; + } + + + + /** + * @return void + * @throws Nette\Application\BadRequestException if no template found + * @throws Nette\Application\AbortException + */ + public function sendTemplate() + { + $template = $this->getTemplate(); + if (!$template) { + return; + } + + if ($template instanceof Nette\Templating\IFileTemplate && !$template->getFile()) { // content template + $files = $this->formatTemplateFiles(); + foreach ($files as $file) { + if (is_file($file)) { + $template->setFile($file); + break; + } + } + + if (!$template->getFile()) { + $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files)); + $file = strtr($file, '/', DIRECTORY_SEPARATOR); + $this->error("Page not found. Missing template '$file'."); + } + } + + $this->sendResponse(new Responses\TextResponse($template)); + } + + + + /** + * Finds layout template file name. + * @return string + */ + public function findLayoutTemplateFile() + { + if ($this->layout === FALSE) { + return; + } + $files = $this->formatLayoutTemplateFiles(); + foreach ($files as $file) { + if (is_file($file)) { + return $file; + } + } + + if ($this->layout) { + $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files)); + $file = strtr($file, '/', DIRECTORY_SEPARATOR); + throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'."); + } + } + + + + /** + * Formats layout template file names. + * @return array + */ + public function formatLayoutTemplateFiles() + { + $name = $this->getName(); + $presenter = substr($name, strrpos(':' . $name, ':')); + $layout = $this->layout ? $this->layout : 'layout'; + $dir = dirname($this->getReflection()->getFileName()); + $dir = is_dir("$dir/templates") ? $dir : dirname($dir); + $list = array( + "$dir/templates/$presenter/@$layout.latte", + "$dir/templates/$presenter.@$layout.latte", + "$dir/templates/$presenter/@$layout.phtml", + "$dir/templates/$presenter.@$layout.phtml", + ); + do { + $list[] = "$dir/templates/@$layout.latte"; + $list[] = "$dir/templates/@$layout.phtml"; + $dir = dirname($dir); + } while ($dir && ($name = substr($name, 0, strrpos($name, ':')))); + return $list; + } + + + + /** + * Formats view template file names. + * @return array + */ + public function formatTemplateFiles() + { + $name = $this->getName(); + $presenter = substr($name, strrpos(':' . $name, ':')); + $dir = dirname($this->getReflection()->getFileName()); + $dir = is_dir("$dir/templates") ? $dir : dirname($dir); + return array( + "$dir/templates/$presenter/$this->view.latte", + "$dir/templates/$presenter.$this->view.latte", + "$dir/templates/$presenter/$this->view.phtml", + "$dir/templates/$presenter.$this->view.phtml", + ); + } + + + + /** + * Formats action method name. + * @param string + * @return string + */ + protected static function formatActionMethod($action) + { + return 'action' . $action; + } + + + + /** + * Formats render view method name. + * @param string + * @return string + */ + protected static function formatRenderMethod($view) + { + return 'render' . $view; + } + + + + /********************* partial AJAX rendering ****************d*g**/ + + + + /** + * @return \stdClass + */ + public function getPayload() + { + return $this->payload; + } + + + + /** + * Is AJAX request? + * @return bool + */ + public function isAjax() + { + if ($this->ajaxMode === NULL) { + $this->ajaxMode = $this->getHttpRequest()->isAjax(); + } + return $this->ajaxMode; + } + + + + /** + * Sends AJAX payload to the output. + * @return void + * @throws Nette\Application\AbortException + */ + public function sendPayload() + { + $this->sendResponse(new Responses\JsonResponse($this->payload)); + } + + + + /********************* navigation & flow ****************d*g**/ + + + + /** + * Sends response and terminates presenter. + * @return void + * @throws Nette\Application\AbortException + */ + public function sendResponse(Application\IResponse $response) + { + $this->response = $response; + $this->terminate(); + } + + + + /** + * Correctly terminates presenter. + * @return void + * @throws Nette\Application\AbortException + */ + public function terminate() + { + if (func_num_args() !== 0) { + trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING); + $this->sendResponse(func_get_arg(0)); + } + throw new Application\AbortException(); + } + + + + /** + * Forward to another presenter or action. + * @param string|Request + * @param array|mixed + * @return void + * @throws Nette\Application\AbortException + */ + public function forward($destination, $args = array()) + { + if ($destination instanceof Application\Request) { + $this->sendResponse(new Responses\ForwardResponse($destination)); + + } elseif (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + + $this->createRequest($this, $destination, $args, 'forward'); + $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest)); + } + + + + /** + * Redirect to another URL and ends presenter execution. + * @param string + * @param int HTTP error code + * @return void + * @throws Nette\Application\AbortException + */ + public function redirectUrl($url, $code = NULL) + { + if ($this->isAjax()) { + $this->payload->redirect = (string) $url; + $this->sendPayload(); + + } elseif (!$code) { + $code = $this->getHttpRequest()->isMethod('post') + ? Http\IResponse::S303_POST_GET + : Http\IResponse::S302_FOUND; + } + $this->sendResponse(new Responses\RedirectResponse($url, $code)); + } + + /** @deprecated */ + function redirectUri($url, $code = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING); + $this->redirectUrl($url, $code); + } + + + + /** + * Throws HTTP error. + * @param string + * @param int HTTP error code + * @return void + * @throws Nette\Application\BadRequestException + */ + public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND) + { + throw new Application\BadRequestException($message, $code); + } + + + + /** + * Link to myself. + * @return string + */ + public function backlink() + { + return $this->getAction(TRUE); + } + + + + /** + * Returns the last created Request. + * @return Nette\Application\Request + */ + public function getLastCreatedRequest() + { + return $this->lastCreatedRequest; + } + + + + /** + * Returns the last created Request flag. + * @param string + * @return bool + */ + public function getLastCreatedRequestFlag($flag) + { + return !empty($this->lastCreatedRequestFlag[$flag]); + } + + + + /** + * Conditional redirect to canonicalized URI. + * @return void + * @throws Nette\Application\AbortException + */ + public function canonicalize() + { + if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) { + try { + $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->getParameters(), 'redirectX'); + } catch (InvalidLinkException $e) {} + if (isset($url) && !$this->getHttpRequest()->getUrl()->isEqual($url)) { + $this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY)); + } + } + } + + + + /** + * Attempts to cache the sent entity by its last modification date. + * @param string|int|DateTime last modified time + * @param string strong entity tag validator + * @param mixed optional expiration time + * @return void + * @throws Nette\Application\AbortException + * @deprecated + */ + public function lastModified($lastModified, $etag = NULL, $expire = NULL) + { + if ($expire !== NULL) { + $this->getHttpResponse()->setExpiration($expire); + } + + if (!$this->getHttpContext()->isModified($lastModified, $etag)) { + $this->terminate(); + } + } + + + + /** + * Request/URL factory. + * @param PresenterComponent base + * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" + * @param array array of arguments + * @param string forward|redirect|link + * @return string URL + * @throws InvalidLinkException + * @internal + */ + final protected function createRequest($component, $destination, array $args, $mode) + { + // note: createRequest supposes that saveState(), run() & tryCall() behaviour is final + + // cached services for better performance + static $presenterFactory, $router, $refUrl; + if ($presenterFactory === NULL) { + $presenterFactory = $this->getApplication()->getPresenterFactory(); + $router = $this->getApplication()->getRouter(); + $refUrl = new Http\Url($this->getHttpRequest()->getUrl()); + $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath()); + } + + $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL; + + // PARSE DESTINATION + // 1) fragment + $a = strpos($destination, '#'); + if ($a === FALSE) { + $fragment = ''; + } else { + $fragment = substr($destination, $a); + $destination = substr($destination, 0, $a); + } + + // 2) ?query syntax + $a = strpos($destination, '?'); + if ($a !== FALSE) { + parse_str(substr($destination, $a + 1), $args); // requires disabled magic quotes + $destination = substr($destination, 0, $a); + } + + // 3) URL scheme + $a = strpos($destination, '//'); + if ($a === FALSE) { + $scheme = FALSE; + } else { + $scheme = substr($destination, 0, $a); + $destination = substr($destination, $a + 2); + } + + // 4) signal or empty + if (!$component instanceof Presenter || substr($destination, -1) === '!') { + $signal = rtrim($destination, '!'); + $a = strrpos($signal, ':'); + if ($a !== FALSE) { + $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-')); + $signal = (string) substr($signal, $a + 1); + } + if ($signal == NULL) { // intentionally == + throw new InvalidLinkException("Signal must be non-empty string."); + } + $destination = 'this'; + } + + if ($destination == NULL) { // intentionally == + throw new InvalidLinkException("Destination must be non-empty string."); + } + + // 5) presenter: action + $current = FALSE; + $a = strrpos($destination, ':'); + if ($a === FALSE) { + $action = $destination === 'this' ? $this->action : $destination; + $presenter = $this->getName(); + $presenterClass = get_class($this); + + } else { + $action = (string) substr($destination, $a + 1); + if ($destination[0] === ':') { // absolute + if ($a < 2) { + throw new InvalidLinkException("Missing presenter name in '$destination'."); + } + $presenter = substr($destination, 1, $a - 1); + + } else { // relative + $presenter = $this->getName(); + $b = strrpos($presenter, ':'); + if ($b === FALSE) { // no module + $presenter = substr($destination, 0, $a); + } else { // with module + $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a); + } + } + try { + $presenterClass = $presenterFactory->getPresenterClass($presenter); + } catch (Application\InvalidPresenterException $e) { + throw new InvalidLinkException($e->getMessage(), NULL, $e); + } + } + + // PROCESS SIGNAL ARGUMENTS + if (isset($signal)) { // $component must be IStatePersistent + $reflection = new PresenterComponentReflection(get_class($component)); + if ($signal === 'this') { // means "no signal" + $signal = ''; + if (array_key_exists(0, $args)) { + throw new InvalidLinkException("Unable to pass parameters to 'this!' signal."); + } + + } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) { // TODO: AppForm exception + // counterpart of signalReceived() & tryCall() + $method = $component->formatSignalMethod($signal); + if (!$reflection->hasCallableMethod($method)) { + throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()"); + } + if ($args) { // convert indexed parameters to named + self::argsToParams(get_class($component), $method, $args); + } + } + + // counterpart of IStatePersistent + if ($args && array_intersect_key($args, $reflection->getPersistentParams())) { + $component->saveState($args); + } + + if ($args && $component !== $this) { + $prefix = $component->getUniqueId() . self::NAME_SEPARATOR; + foreach ($args as $key => $val) { + unset($args[$key]); + $args[$prefix . $key] = $val; + } + } + } + + // PROCESS ARGUMENTS + if (is_subclass_of($presenterClass, __CLASS__)) { + if ($action === '') { + $action = self::DEFAULT_ACTION; + } + + $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this); // TODO + + $reflection = new PresenterComponentReflection($presenterClass); + if ($args || $destination === 'this') { + // counterpart of run() & tryCall() + /**/$method = $presenterClass::formatActionMethod($action);/**/ + /*5.2* $method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);*/ + if (!$reflection->hasCallableMethod($method)) { + /**/$method = $presenterClass::formatRenderMethod($action);/**/ + /*5.2* $method = call_user_func(array($presenterClass, 'formatRenderMethod'), $action);*/ + if (!$reflection->hasCallableMethod($method)) { + $method = NULL; + } + } + + // convert indexed parameters to named + if ($method === NULL) { + if (array_key_exists(0, $args)) { + throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method."); + } + + } elseif ($destination === 'this') { + self::argsToParams($presenterClass, $method, $args, $this->params); + + } else { + self::argsToParams($presenterClass, $method, $args); + } + } + + // counterpart of IStatePersistent + if ($args && array_intersect_key($args, $reflection->getPersistentParams())) { + $this->saveState($args, $reflection); + } + + if ($mode === 'redirect') { + $this->saveGlobalState(); + } + + $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass); + if ($current && $args) { + $tmp = $globalState + $this->params; + foreach ($args as $key => $val) { + if (http_build_query(array($val)) !== (isset($tmp[$key]) ? http_build_query(array($tmp[$key])) : '')) { + $current = FALSE; + break; + } + } + } + $args += $globalState; + } + + // ADD ACTION & SIGNAL & FLASH + $args[self::ACTION_KEY] = $action; + if (!empty($signal)) { + $args[self::SIGNAL_KEY] = $component->getParameterId($signal); + $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY); + } + if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) { + $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY); + } + + $this->lastCreatedRequest = new Application\Request( + $presenter, + Application\Request::FORWARD, + $args, + array(), + array() + ); + $this->lastCreatedRequestFlag = array('current' => $current); + + if ($mode === 'forward') { + return; + } + + // CONSTRUCT URL + $url = $router->constructUrl($this->lastCreatedRequest, $refUrl); + if ($url === NULL) { + unset($args[self::ACTION_KEY]); + $params = urldecode(http_build_query($args, NULL, ', ')); + throw new InvalidLinkException("No route for $presenter:$action($params)"); + } + + // make URL relative if possible + if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) { + $hostUrl = $refUrl->getHostUrl(); + if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) { + $url = substr($url, strlen($hostUrl)); + } + } + + return $url . $fragment; + } + + + + /** + * Converts list of arguments to named parameters. + * @param string class name + * @param string method name + * @param array arguments + * @param array supplemental arguments + * @return void + * @throws InvalidLinkException + */ + private static function argsToParams($class, $method, & $args, $supplemental = array()) + { + $i = 0; + $rm = new \ReflectionMethod($class, $method); + foreach ($rm->getParameters() as $param) { + $name = $param->getName(); + if (array_key_exists($i, $args)) { + $args[$name] = $args[$i]; + unset($args[$i]); + $i++; + + } elseif (array_key_exists($name, $args)) { + // continue with process + + } elseif (array_key_exists($name, $supplemental)) { + $args[$name] = $supplemental[$name]; + + } else { + continue; + } + + if ($args[$name] === NULL) { + continue; + } + + $def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL; // see PHP bug #62988 + $type = $param->isArray() ? 'array' : gettype($def); + if (!PresenterComponentReflection::convertType($args[$name], $type)) { + throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + + if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) { + $args[$name] = NULL; // value transmit is unnecessary + } + } + + if (array_key_exists($i, $args)) { + $method = $rm->getName(); + throw new InvalidLinkException("Passed more parameters than method $class::$method() expects."); + } + } + + + + /** + * Invalid link handler. Descendant can override this method to change default behaviour. + * @return string + * @throws InvalidLinkException + */ + protected function handleInvalidLink(InvalidLinkException $e) + { + if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) { + return '#'; + + } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) { + return 'error: ' . $e->getMessage(); + + } else { // self::INVALID_LINK_EXCEPTION + throw $e; + } + } + + + + /********************* request serialization ****************d*g**/ + + + + /** + * Stores current request to session. + * @param mixed optional expiration time + * @return string key + */ + public function storeRequest($expiration = '+ 10 minutes') + { + $session = $this->getSession('Nette.Application/requests'); + do { + $key = Nette\Utils\Strings::random(5); + } while (isset($session[$key])); + + $session[$key] = array($this->getUser()->getId(), $this->request); + $session->setExpiration($expiration, $key); + return $key; + } + + + + /** + * Restores current request to session. + * @param string key + * @return void + */ + public function restoreRequest($key) + { + $session = $this->getSession('Nette.Application/requests'); + if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->getUser()->getId())) { + return; + } + $request = clone $session[$key][1]; + unset($session[$key]); + $request->setFlag(Application\Request::RESTORED, TRUE); + $params = $request->getParameters(); + $params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY); + $request->setParameters($params); + $this->sendResponse(new Responses\ForwardResponse($request)); + } + + + + /********************* interface IStatePersistent ****************d*g**/ + + + + /** + * Returns array of persistent components. + * This default implementation detects components by class-level annotation @persistent(cmp1, cmp2). + * @return array + */ + public static function getPersistentComponents() + { + /*5.2*$arg = func_get_arg(0);*/ + return (array) Reflection\ClassType::from(/*5.2*$arg*//**/get_called_class()/**/) + ->getAnnotation('persistent'); + } + + + + /** + * Saves state information for all subcomponents to $this->globalState. + * @return array + */ + private function getGlobalState($forClass = NULL) + { + $sinces = & $this->globalStateSinces; + + if ($this->globalState === NULL) { + $state = array(); + foreach ($this->globalParams as $id => $params) { + $prefix = $id . self::NAME_SEPARATOR; + foreach ($params as $key => $val) { + $state[$prefix . $key] = $val; + } + } + $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL); + + if ($sinces === NULL) { + $sinces = array(); + foreach ($this->getReflection()->getPersistentParams() as $name => $meta) { + $sinces[$name] = $meta['since']; + } + } + + $components = $this->getReflection()->getPersistentComponents(); + $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent'); + + foreach ($iterator as $name => $component) { + if ($iterator->getDepth() === 0) { + // counts with Nette\Application\RecursiveIteratorIterator::SELF_FIRST + $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE; // FALSE = nonpersistent + } + $prefix = $component->getUniqueId() . self::NAME_SEPARATOR; + $params = array(); + $component->saveState($params); + foreach ($params as $key => $val) { + $state[$prefix . $key] = $val; + $sinces[$prefix . $key] = $since; + } + } + + } else { + $state = $this->globalState; + } + + if ($forClass !== NULL) { + $since = NULL; + foreach ($state as $key => $foo) { + if (!isset($sinces[$key])) { + $x = strpos($key, self::NAME_SEPARATOR); + $x = $x === FALSE ? $key : substr($key, 0, $x); + $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE; + } + if ($since !== $sinces[$key]) { + $since = $sinces[$key]; + $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since); + } + if (!$ok) { + unset($state[$key]); + } + } + } + + return $state; + } + + + + /** + * Permanently saves state information for all subcomponents to $this->globalState. + * @return void + */ + protected function saveGlobalState() + { + // load lazy components + foreach ($this->globalParams as $id => $foo) { + $this->getComponent($id, FALSE); + } + + $this->globalParams = array(); + $this->globalState = $this->getGlobalState(); + } + + + + /** + * Initializes $this->globalParams, $this->signal & $this->signalReceiver, $this->action, $this->view. Called by run(). + * @return void + * @throws Nette\Application\BadRequestException if action name is not valid + */ + private function initGlobalParameters() + { + // init $this->globalParams + $this->globalParams = array(); + $selfParams = array(); + + $params = $this->request->getParameters(); + if ($this->isAjax()) { + $params += $this->request->getPost(); + } + + foreach ($params as $key => $value) { + if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+$)[a-z0-9_]+)$#i', $key, $matches)) { + $this->error("'Invalid parameter name '$key'"); + } + if (!$matches[1]) { + $selfParams[$key] = $value; + } else { + $this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value; + } + } + + // init & validate $this->action & $this->view + $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION); + + // init $this->signalReceiver and key 'signal' in appropriate params array + $this->signalReceiver = $this->getUniqueId(); + if (isset($selfParams[self::SIGNAL_KEY])) { + $param = $selfParams[self::SIGNAL_KEY]; + if (!is_string($param)) { + $this->error('Signal name is not string.'); + } + $pos = strrpos($param, '-'); + if ($pos) { + $this->signalReceiver = substr($param, 0, $pos); + $this->signal = substr($param, $pos + 1); + } else { + $this->signalReceiver = $this->getUniqueId(); + $this->signal = $param; + } + if ($this->signal == NULL) { // intentionally == + $this->signal = NULL; + } + } + + $this->loadState($selfParams); + } + + + + /** + * Pops parameters for specified component. + * @param string component id + * @return array + */ + final public function popGlobalParameters($id) + { + if (isset($this->globalParams[$id])) { + $res = $this->globalParams[$id]; + unset($this->globalParams[$id]); + return $res; + + } else { + return array(); + } + } + + + + /********************* flash session ****************d*g**/ + + + + /** + * Checks if a flash session namespace exists. + * @return bool + */ + public function hasFlashSession() + { + return !empty($this->params[self::FLASH_KEY]) + && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); + } + + + + /** + * Returns session namespace provided to pass temporary data between redirects. + * @return Nette\Http\SessionSection + */ + public function getFlashSession() + { + if (empty($this->params[self::FLASH_KEY])) { + $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4); + } + return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); + } + + + + /********************* services ****************d*g**/ + + + + /** + * @return void + */ + final public function injectPrimary(Nette\DI\Container $context) + { + $this->context = $context; + } + + + + /** + * Gets the context. + * @return \SystemContainer|Nette\DI\Container + */ + final public function getContext() + { + return $this->context; + } + + + + /** + * @deprecated + */ + final public function getService($name) + { + return $this->context->getService($name); + } + + + + /** + * @return Nette\Http\Request + */ + protected function getHttpRequest() + { + return $this->context->getByType('Nette\Http\IRequest'); + } + + + + /** + * @return Nette\Http\Response + */ + protected function getHttpResponse() + { + return $this->context->getByType('Nette\Http\IResponse'); + } + + + + /** + * @return Nette\Http\Context + */ + protected function getHttpContext() + { + return $this->context->getByType('Nette\Http\Context'); + } + + + + /** + * @return Nette\Application\Application + */ + public function getApplication() + { + return $this->context->getByType('Nette\Application\Application'); + } + + + + /** + * @return Nette\Http\Session + */ + public function getSession($namespace = NULL) + { + $handler = $this->context->getByType('Nette\Http\Session'); + return $namespace === NULL ? $handler : $handler->getSection($namespace); + } + + + + /** + * @return Nette\Security\User + */ + public function getUser() + { + return $this->context->getByType('Nette\Security\User'); + } + +} diff --git a/libs/Nette/Application/UI/PresenterComponent.php b/libs/Nette/Application/UI/PresenterComponent.php index a091b59..1cd6f3c 100644 --- a/libs/Nette/Application/UI/PresenterComponent.php +++ b/libs/Nette/Application/UI/PresenterComponent.php @@ -1,427 +1,450 @@ -monitor('Nette\Application\UI\Presenter'); - parent::__construct($parent, $name); - } - - - - /** - * Returns the presenter where this component belongs to. - * @param bool throw exception if presenter doesn't exist? - * @return Presenter|NULL - */ - public function getPresenter($need = TRUE) - { - return $this->lookup('Nette\Application\UI\Presenter', $need); - } - - - - /** - * Returns a fully-qualified name that uniquely identifies the component - * within the presenter hierarchy. - * @return string - */ - public function getUniqueId() - { - return $this->lookupPath('Nette\Application\UI\Presenter', TRUE); - } - - - - /** - * This method will be called when the component (or component's parent) - * becomes attached to a monitored object. Do not call this method yourself. - * @param Nette\Application\IComponent - * @return void - */ - protected function attached($presenter) - { - if ($presenter instanceof Presenter) { - $this->loadState($presenter->popGlobalParams($this->getUniqueId())); - } - } - - - - /** - * Calls public method if exists. - * @param string - * @param array - * @return bool does method exist? - */ - protected function tryCall($method, array $params) - { - $rc = $this->getReflection(); - if ($rc->hasMethod($method)) { - $rm = $rc->getMethod($method); - if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) { - $rm->invokeNamedArgs($this, $params); - return TRUE; - } - } - return FALSE; - } - - - - /** - * Access to reflection. - * @return PresenterComponentReflection - */ - public static function getReflection() - { - return new PresenterComponentReflection(get_called_class()); - } - - - - /********************* interface IStatePersistent ****************d*g**/ - - - - /** - * Loads state informations. - * @param array - * @return void - */ - public function loadState(array $params) - { - foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) { - if (isset($params[$nm])) { // ignore NULL values - if (isset($meta['def'])) { - if (is_array($params[$nm]) && !is_array($meta['def'])) { - $params[$nm] = $meta['def']; // prevents array to scalar conversion - } else { - settype($params[$nm], gettype($meta['def'])); - } - } - $this->$nm = & $params[$nm]; - } - } - $this->params = $params; - } - - - - /** - * Saves state informations for next request. - * @param array - * @param PresenterComponentReflection (internal, used by Presenter) - * @return void - */ - public function saveState(array & $params, $reflection = NULL) - { - $reflection = $reflection === NULL ? $this->getReflection() : $reflection; - foreach ($reflection->getPersistentParams() as $nm => $meta) { - - if (isset($params[$nm])) { - $val = $params[$nm]; // injected value - - } elseif (array_key_exists($nm, $params)) { // $params[$nm] === NULL - continue; // means skip - - } elseif (!isset($meta['since']) || $this instanceof $meta['since']) { - $val = $this->$nm; // object property value - - } else { - continue; // ignored parameter - } - - if (is_object($val)) { - $class = get_class($this); - throw new Nette\InvalidStateException("Persistent parameter must be scalar or array, $class::\$$nm is " . gettype($val)); - - } else { - if (isset($meta['def'])) { - settype($val, gettype($meta['def'])); - if ($val === $meta['def']) { - $val = NULL; - } - } else { - if ((string) $val === '') { - $val = NULL; - } - } - $params[$nm] = $val; - } - } - } - - - - /** - * Returns component param. - * If no key is passed, returns the entire array. - * @param string key - * @param mixed default value - * @return mixed - */ - final public function getParam($name = NULL, $default = NULL) - { - if (func_num_args() === 0) { - return $this->params; - - } elseif (isset($this->params[$name])) { - return $this->params[$name]; - - } else { - return $default; - } - } - - - - /** - * Returns a fully-qualified name that uniquely identifies the parameter. - * @return string - */ - final public function getParamId($name) - { - $uid = $this->getUniqueId(); - return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name; - } - - - - /** - * Returns array of classes persistent parameters. They have public visibility and are non-static. - * This default implementation detects persistent parameters by annotation @persistent. - * @return array - */ - public static function getPersistentParams() - { - $rc = new Nette\Reflection\ClassType(get_called_class()); - $params = array(); - foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) { - if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) { - $params[] = $rp->getName(); - } - } - return $params; - } - - - - /********************* interface ISignalReceiver ****************d*g**/ - - - - /** - * Calls signal handler method. - * @param string - * @return void - * @throws BadSignalException if there is not handler method - */ - public function signalReceived($signal) - { - if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) { - $class = get_class($this); - throw new BadSignalException("There is no handler for signal '$signal' in class $class."); - } - } - - - - /** - * Formats signal handler method name -> case sensitivity doesn't matter. - * @param string - * @return string - */ - public function formatSignalMethod($signal) - { - return $signal == NULL ? NULL : 'handle' . $signal; // intentionally == - } - - - - /********************* navigation ****************d*g**/ - - - - /** - * Generates URL to presenter, action or signal. - * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" - * @param array|mixed - * @return string - * @throws InvalidLinkException - */ - public function link($destination, $args = array()) - { - if (!is_array($args)) { - $args = func_get_args(); - array_shift($args); - } - - try { - return $this->getPresenter()->createRequest($this, $destination, $args, 'link'); - - } catch (InvalidLinkException $e) { - return $this->getPresenter()->handleInvalidLink($e); - } - } - - - - /** - * Returns destination as Link object. - * @param string destination in format "[[module:]presenter:]view" or "signal!" - * @param array|mixed - * @return Link - */ - public function lazyLink($destination, $args = array()) - { - if (!is_array($args)) { - $args = func_get_args(); - array_shift($args); - } - - return new Link($this, $destination, $args); - } - - - - /** - * Determines whether it links to the current page. - * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" - * @param array|mixed - * @return bool - * @throws InvalidLinkException - */ - public function isLinkCurrent($destination = NULL, $args = array()) - { - if ($destination !== NULL) { - if (!is_array($args)) { - $args = func_get_args(); - array_shift($args); - } - $this->link($destination, $args); - } - return $this->getPresenter()->getLastCreatedRequestFlag('current'); - } - - - - /** - * Redirect to another presenter, action or signal. - * @param int [optional] HTTP error code - * @param string destination in format "[[module:]presenter:]view" or "signal!" - * @param array|mixed - * @return void - * @throws Nette\Application\AbortException - */ - public function redirect($code, $destination = NULL, $args = array()) - { - if (!is_numeric($code)) { // first parameter is optional - $args = $destination; - $destination = $code; - $code = NULL; - } - - if (!is_array($args)) { - $args = func_get_args(); - if (is_numeric(array_shift($args))) { - array_shift($args); - } - } - - $presenter = $this->getPresenter(); - $presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code); - } - - - - /********************* interface \ArrayAccess ****************d*g**/ - - - - /** - * Adds the component to the container. - * @param string component name - * @param Nette\ComponentModel\IComponent - * @return void - */ - final public function offsetSet($name, $component) - { - $this->addComponent($component, $name); - } - - - - /** - * Returns component specified by name. Throws exception if component doesn't exist. - * @param string component name - * @return Nette\ComponentModel\IComponent - * @throws Nette\InvalidArgumentException - */ - final public function offsetGet($name) - { - return $this->getComponent($name, TRUE); - } - - - - /** - * Does component specified by name exists? - * @param string component name - * @return bool - */ - final public function offsetExists($name) - { - return $this->getComponent($name, FALSE) !== NULL; - } - - - - /** - * Removes component from the container. - * @param string component name - * @return void - */ - final public function offsetUnset($name) - { - $component = $this->getComponent($name, FALSE); - if ($component !== NULL) { - $this->removeComponent($component); - } - } - -} +monitor('Nette\Application\UI\Presenter'); + parent::__construct($parent, $name); + } + + + + /** + * Returns the presenter where this component belongs to. + * @param bool throw exception if presenter doesn't exist? + * @return Presenter|NULL + */ + public function getPresenter($need = TRUE) + { + return $this->lookup('Nette\Application\UI\Presenter', $need); + } + + + + /** + * Returns a fully-qualified name that uniquely identifies the component + * within the presenter hierarchy. + * @return string + */ + public function getUniqueId() + { + return $this->lookupPath('Nette\Application\UI\Presenter', TRUE); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param Nette\ComponentModel\IComponent + * @return void + */ + protected function attached($presenter) + { + if ($presenter instanceof Presenter) { + $this->loadState($presenter->popGlobalParameters($this->getUniqueId())); + } + } + + + + /** + * Calls public method if exists. + * @param string + * @param array + * @return bool does method exist? + */ + protected function tryCall($method, array $params) + { + $rc = $this->getReflection(); + if ($rc->hasMethod($method)) { + $rm = $rc->getMethod($method); + if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) { + $this->checkRequirements($rm); + $rm->invokeArgs($this, $rc->combineArgs($rm, $params)); + return TRUE; + } + } + return FALSE; + } + + + + /** + * Checks for requirements such as authorization. + * @return void + */ + public function checkRequirements($element) + { + } + + + + /** + * Access to reflection. + * @return PresenterComponentReflection + */ + public /**/static/**/ function getReflection() + { + return new PresenterComponentReflection(/*5.2*$this*//**/get_called_class()/**/); + } + + + + /********************* interface IStatePersistent ****************d*g**/ + + + + /** + * Loads state informations. + * @param array + * @return void + */ + public function loadState(array $params) + { + $reflection = $this->getReflection(); + foreach ($reflection->getPersistentParams() as $name => $meta) { + if (isset($params[$name])) { // NULLs are ignored + $type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x + if (!$reflection->convertType($params[$name], $type)) { + throw new Nette\Application\BadRequestException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + $this->$name = & $params[$name]; + } else { + $params[$name] = & $this->$name; + } + } + $this->params = $params; + } + + + + /** + * Saves state informations for next request. + * @param array + * @param PresenterComponentReflection (internal, used by Presenter) + * @return void + */ + public function saveState(array & $params, $reflection = NULL) + { + $reflection = $reflection === NULL ? $this->getReflection() : $reflection; + foreach ($reflection->getPersistentParams() as $name => $meta) { + + if (isset($params[$name])) { + // injected value + + } elseif (array_key_exists($name, $params)) { // NULLs are skipped + continue; + + } elseif (!isset($meta['since']) || $this instanceof $meta['since']) { + $params[$name] = $this->$name; // object property value + + } else { + continue; // ignored parameter + } + + $type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x + if (!PresenterComponentReflection::convertType($params[$name], $type)) { + throw new InvalidLinkException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + + if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) { + $params[$name] = NULL; // value transmit is unnecessary + } + } + } + + + + /** + * Returns component param. + * If no key is passed, returns the entire array. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getParameter($name = NULL, $default = NULL) + { + if (func_num_args() === 0) { + return $this->params; + + } elseif (isset($this->params[$name])) { + return $this->params[$name]; + + } else { + return $default; + } + } + + + + /** + * Returns a fully-qualified name that uniquely identifies the parameter. + * @param string + * @return string + */ + final public function getParameterId($name) + { + $uid = $this->getUniqueId(); + return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name; + } + + + + /** @deprecated */ + function getParam($name = NULL, $default = NULL) + { + //trigger_error(__METHOD__ . '() is deprecated; use getParameter() instead.', E_USER_WARNING); + return func_num_args() ? $this->getParameter($name, $default) : $this->getParameter(); + } + + + + /** @deprecated */ + function getParamId($name) + { + trigger_error(__METHOD__ . '() is deprecated; use getParameterId() instead.', E_USER_WARNING); + return $this->getParameterId($name); + } + + + + /** + * Returns array of classes persistent parameters. They have public visibility and are non-static. + * This default implementation detects persistent parameters by annotation @persistent. + * @return array + */ + public static function getPersistentParams() + { + /*5.2*$arg = func_get_arg(0);*/ + $rc = new Nette\Reflection\ClassType(/*5.2*$arg*//**/get_called_class()/**/); + $params = array(); + foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) { + if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) { + $params[] = $rp->getName(); + } + } + return $params; + } + + + + /********************* interface ISignalReceiver ****************d*g**/ + + + + /** + * Calls signal handler method. + * @param string + * @return void + * @throws BadSignalException if there is not handler method + */ + public function signalReceived($signal) + { + if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) { + $class = get_class($this); + throw new BadSignalException("There is no handler for signal '$signal' in class $class."); + } + } + + + + /** + * Formats signal handler method name -> case sensitivity doesn't matter. + * @param string + * @return string + */ + public function formatSignalMethod($signal) + { + return $signal == NULL ? NULL : 'handle' . $signal; // intentionally == + } + + + + /********************* navigation ****************d*g**/ + + + + /** + * Generates URL to presenter, action or signal. + * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" + * @param array|mixed + * @return string + * @throws InvalidLinkException + */ + public function link($destination, $args = array()) + { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + + try { + return $this->getPresenter()->createRequest($this, $destination, $args, 'link'); + + } catch (InvalidLinkException $e) { + return $this->getPresenter()->handleInvalidLink($e); + } + } + + + + /** + * Returns destination as Link object. + * @param string destination in format "[[module:]presenter:]view" or "signal!" + * @param array|mixed + * @return Link + */ + public function lazyLink($destination, $args = array()) + { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + + return new Link($this, $destination, $args); + } + + + + /** + * Determines whether it links to the current page. + * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" + * @param array|mixed + * @return bool + * @throws InvalidLinkException + */ + public function isLinkCurrent($destination = NULL, $args = array()) + { + if ($destination !== NULL) { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + $this->link($destination, $args); + } + return $this->getPresenter()->getLastCreatedRequestFlag('current'); + } + + + + /** + * Redirect to another presenter, action or signal. + * @param int [optional] HTTP error code + * @param string destination in format "[[module:]presenter:]view" or "signal!" + * @param array|mixed + * @return void + * @throws Nette\Application\AbortException + */ + public function redirect($code, $destination = NULL, $args = array()) + { + if (!is_numeric($code)) { // first parameter is optional + $args = $destination; + $destination = $code; + $code = NULL; + } + + if (!is_array($args)) { + $args = func_get_args(); + if (is_numeric(array_shift($args))) { + array_shift($args); + } + } + + $presenter = $this->getPresenter(); + $presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code); + } + + + + /********************* interface \ArrayAccess ****************d*g**/ + + + + /** + * Adds the component to the container. + * @param string component name + * @param Nette\ComponentModel\IComponent + * @return void + */ + final public function offsetSet($name, $component) + { + $this->addComponent($component, $name); + } + + + + /** + * Returns component specified by name. Throws exception if component doesn't exist. + * @param string component name + * @return Nette\ComponentModel\IComponent + * @throws Nette\InvalidArgumentException + */ + final public function offsetGet($name) + { + return $this->getComponent($name, TRUE); + } + + + + /** + * Does component specified by name exists? + * @param string component name + * @return bool + */ + final public function offsetExists($name) + { + return $this->getComponent($name, FALSE) !== NULL; + } + + + + /** + * Removes component from the container. + * @param string component name + * @return void + */ + final public function offsetUnset($name) + { + $component = $this->getComponent($name, FALSE); + if ($component !== NULL) { + $this->removeComponent($component); + } + } + +} diff --git a/libs/Nette/Application/UI/PresenterComponentReflection.php b/libs/Nette/Application/UI/PresenterComponentReflection.php index 0621025..579b7cb 100644 --- a/libs/Nette/Application/UI/PresenterComponentReflection.php +++ b/libs/Nette/Application/UI/PresenterComponentReflection.php @@ -1,119 +1,174 @@ -getName() : $class; // TODO - $params = & self::$ppCache[$class]; - if ($params !== NULL) { - return $params; - } - $params = array(); - if (is_subclass_of($class, 'Nette\Application\UI\PresenterComponent')) { - // $class::getPersistentParams() in PHP 5.3 - $defaults = get_class_vars($class); - foreach (call_user_func(array($class, 'getPersistentParams'), $class) as $name => $meta) { - if (is_string($meta)) { - $name = $meta; - } - $params[$name] = array( - 'def' => $defaults[$name], - 'since' => $class, - ); - } - foreach ($this->getPersistentParams(get_parent_class($class)) as $name => $param) { - if (isset($params[$name])) { - $params[$name]['since'] = $param['since']; - continue; - } - - $params[$name] = $param; - } - } - return $params; - } - - - - /** - * @return array of persistent components. - */ - public function getPersistentComponents() - { - $class = $this->getName(); - $components = & self::$pcCache[$class]; - if ($components !== NULL) { - return $components; - } - $components = array(); - if (is_subclass_of($class, 'Nette\Application\UI\Presenter')) { - // $class::getPersistentComponents() in PHP 5.3 - foreach (call_user_func(array($class, 'getPersistentComponents'), $class) as $name => $meta) { - if (is_string($meta)) { - $name = $meta; - } - $components[$name] = array('since' => $class); - } - $components = self::getPersistentComponents(get_parent_class($class)) + $components; - } - return $components; - } - - - - /** - * Is a method callable? It means class is instantiable and method has - * public visibility, is non-static and non-abstract. - * @param string method name - * @return bool - */ - public function hasCallableMethod($method) - { - $class = $this->getName(); - $cache = & self::$mcCache[strtolower($class . ':' . $method)]; - if ($cache === NULL) try { - $cache = FALSE; - $rm = Nette\Reflection\Method::from($class, $method); - $cache = $this->isInstantiable() && $rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic(); - } catch (\ReflectionException $e) { - } - return $cache; - } - -} +getName() : $class; // TODO + $params = & self::$ppCache[$class]; + if ($params !== NULL) { + return $params; + } + $params = array(); + if (is_subclass_of($class, 'Nette\Application\UI\PresenterComponent')) { + $defaults = get_class_vars($class); + foreach (/**/$class::getPersistentParams()/**//*5.2*call_user_func(array($class, 'getPersistentParams'), $class)*/ as $name => $meta) { + if (is_string($meta)) { + $name = $meta; + } + $params[$name] = array( + 'def' => $defaults[$name], + 'since' => $class, + ); + } + foreach ($this->getPersistentParams(get_parent_class($class)) as $name => $param) { + if (isset($params[$name])) { + $params[$name]['since'] = $param['since']; + continue; + } + + $params[$name] = $param; + } + } + return $params; + } + + + + /** + * @param string|NULL + * @return array of persistent components. + */ + public function getPersistentComponents($class = NULL) + { + $class = $class === NULL ? $this->getName() : $class; + $components = & self::$pcCache[$class]; + if ($components !== NULL) { + return $components; + } + $components = array(); + if (is_subclass_of($class, 'Nette\Application\UI\Presenter')) { + foreach (/**/$class::getPersistentComponents()/**//*5.2*call_user_func(array($class, 'getPersistentComponents'), $class)*/ as $name => $meta) { + if (is_string($meta)) { + $name = $meta; + } + $components[$name] = array('since' => $class); + } + $components = $this->getPersistentComponents(get_parent_class($class)) + $components; + } + return $components; + } + + + + /** + * Is a method callable? It means class is instantiable and method has + * public visibility, is non-static and non-abstract. + * @param string method name + * @return bool + */ + public function hasCallableMethod($method) + { + $class = $this->getName(); + $cache = & self::$mcCache[strtolower($class . ':' . $method)]; + if ($cache === NULL) try { + $cache = FALSE; + $rm = Nette\Reflection\Method::from($class, $method); + $cache = $this->isInstantiable() && $rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic(); + } catch (\ReflectionException $e) { + } + return $cache; + } + + + + /** + * @return array + */ + public static function combineArgs(\ReflectionFunctionAbstract $method, $args) + { + $res = array(); + $i = 0; + foreach ($method->getParameters() as $param) { + $name = $param->getName(); + if (isset($args[$name])) { // NULLs are ignored + $res[$i++] = $args[$name]; + $type = $param->isArray() ? 'array' : ($param->isDefaultValueAvailable() && $param->isOptional() ? gettype($param->getDefaultValue()) : 'NULL'); + if (!self::convertType($res[$i-1], $type)) { + $mName = $method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' . $method->getName() : $method->getName(); + throw new BadRequestException("Invalid value for parameter '$name' in method $mName(), expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + } else { + $res[$i++] = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : ($param->isArray() ? array() : NULL); + } + } + return $res; + } + + + + /** + * Non data-loss type conversion. + * @param mixed + * @param string + * @return bool + */ + public static function convertType(& $val, $type) + { + if ($val === NULL || is_object($val)) { + // ignore + } elseif ($type === 'array') { + if (!is_array($val)) { + return FALSE; + } + } elseif (!is_scalar($val)) { + return FALSE; + + } elseif ($type !== 'NULL') { + $old = $val = ($val === FALSE ? '0' : (string) $val); + settype($val, $type); + if ($old !== ($val === FALSE ? '0' : (string) $val)) { + return FALSE; // data-loss occurs + } + } + return TRUE; + } + +} diff --git a/libs/Nette/Application/exceptions.php b/libs/Nette/Application/exceptions.php index 9a0774b..d3d1760 100644 --- a/libs/Nette/Application/exceptions.php +++ b/libs/Nette/Application/exceptions.php @@ -1,78 +1,92 @@ - 504) { - $code = $this->defaultCode; - } - - { - parent::__construct($message, $code, $previous); - } - } - -} - - - -/** - * Forbidden request exception - access denied. - */ -class ForbiddenRequestException extends BadRequestException -{ - /** @var int */ - protected $defaultCode = 403; - -} +previous = $previous; + parent::__construct($message, $code); + } else { + parent::__construct($message, $code, $previous); + } + } + */ +} + + + +/** + * The exception that is thrown when a presenter cannot be loaded. + */ +class InvalidPresenterException extends \Exception +{ +} + + + +/** + * Bad HTTP / presenter request exception. + */ +class BadRequestException extends \Exception +{ + /** @var int */ + protected $defaultCode = 404; + + + public function __construct($message = '', $code = 0, \Exception $previous = NULL) + { + if ($code < 200 || $code > 504) { + $code = $this->defaultCode; + } + + /*5.2*if (PHP_VERSION_ID < 50300) { + $this->previous = $previous; + parent::__construct($message, $code); + } else*/ { + parent::__construct($message, $code, $previous); + } + } + +} + + + +/** + * Forbidden request exception - access denied. + */ +class ForbiddenRequestException extends BadRequestException +{ + /** @var int */ + protected $defaultCode = 403; + +} diff --git a/libs/Nette/Application/templates/error.phtml b/libs/Nette/Application/templates/error.phtml index 1d58621..62eb652 100644 --- a/libs/Nette/Application/templates/error.phtml +++ b/libs/Nette/Application/templates/error.phtml @@ -1,32 +1,37 @@ - array('Oops...', 'Your browser sent a request that this server could not understand or process.'), - 403 => array('Access Denied', 'You do not have permission to view this page. Please try contact the web site administrator if you believe you should be able to view this page.'), - 404 => array('Page Not Found', 'The page you requested could not be found. It is possible that the address is incorrect, or that the page no longer exists. Please use a search engine to find what you are looking for.'), - 405 => array('Method Not Allowed', 'The requested method is not allowed for the URL.'), - 410 => array('Page Not Found', 'The page you requested has been taken off the site. We apologize for the inconvenience.'), - 500 => array('Server Error', 'We\'re sorry! The server encountered an internal error and was unable to complete your request. Please try again later.'), -); -$message = isset($messages[$code]) ? $messages[$code] : $messages[0]; - -?> - - - - - -<?php echo $message[0] ?> - -

    - -

    - -

    error

    + array('Oops...', 'Your browser sent a request that this server could not understand or process.'), + 403 => array('Access Denied', 'You do not have permission to view this page. Please try contact the web site administrator if you believe you should be able to view this page.'), + 404 => array('Page Not Found', 'The page you requested could not be found. It is possible that the address is incorrect, or that the page no longer exists. Please use a search engine to find what you are looking for.'), + 405 => array('Method Not Allowed', 'The requested method is not allowed for the URL.'), + 410 => array('Page Not Found', 'The page you requested has been taken off the site. We apologize for the inconvenience.'), + 500 => array('Server Error', 'We\'re sorry! The server encountered an internal error and was unable to complete your request. Please try again later.'), +); +$message = isset($messages[$code]) ? $messages[$code] : $messages[0]; + +?> + + + + + +<?php echo $message[0] ?> + +

    + +

    + +

    error

    diff --git a/libs/Nette/Caching/Cache.php b/libs/Nette/Caching/Cache.php index 8e22a7a..d746acb 100644 --- a/libs/Nette/Caching/Cache.php +++ b/libs/Nette/Caching/Cache.php @@ -1,368 +1,428 @@ -storage = $storage; - $this->namespace = $namespace . self::NAMESPACE_SEPARATOR; - } - - - - /** - * Returns cache storage. - * @return IStorage - */ - public function getStorage() - { - return $this->storage; - } - - - - /** - * Returns cache namespace. - * @return string - */ - public function getNamespace() - { - return (string) substr($this->namespace, 0, -1); - } - - - - /** - * Returns new nested cache object. - * @param string - * @return Cache - */ - public function derive($namespace) - { - $derived = new static($this->storage, $this->namespace . $namespace); - return $derived; - } - - - - /** - * Discards the internal cache. - * @return void - */ - public function release() - { - $this->key = $this->data = NULL; - } - - - - /** - * Retrieves the specified item from the cache or returns NULL if the key is not found. - * @param mixed key - * @return mixed|NULL - */ - public function load($key) - { - $key = is_scalar($key) ? (string) $key : serialize($key); - if ($this->key === $key) { - return $this->data; - } - $this->key = $key; - $this->data = $this->storage->read($this->namespace . md5($key)); - return $this->data; - } - - - - /** - * Writes item into the cache. - * Dependencies are: - * - Cache::PRIORITY => (int) priority - * - Cache::EXPIRATION => (timestamp) expiration - * - Cache::SLIDING => (bool) use sliding expiration? - * - Cache::TAGS => (array) tags - * - Cache::FILES => (array|string) file names - * - Cache::ITEMS => (array|string) cache items - * - Cache::CONSTS => (array|string) cache items - * - * @param mixed key - * @param mixed value - * @param array dependencies - * @return mixed value itself - * @throws Nette\InvalidArgumentException - */ - public function save($key, $data, array $dp = NULL) - { - $this->key = is_scalar($key) ? (string) $key : serialize($key); - $key = $this->namespace . md5($this->key); - - // convert expire into relative amount of seconds - if (isset($dp[Cache::EXPIRATION])) { - $dp[Cache::EXPIRATION] = Nette\DateTime::from($dp[Cache::EXPIRATION])->format('U') - time(); - } - - // convert FILES into CALLBACKS - if (isset($dp[self::FILES])) { - //clearstatcache(); - foreach ((array) $dp[self::FILES] as $item) { - $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); // @ - stat may fail - } - unset($dp[self::FILES]); - } - - // add namespaces to items - if (isset($dp[self::ITEMS])) { - $dp[self::ITEMS] = (array) $dp[self::ITEMS]; - foreach ($dp[self::ITEMS] as $k => $item) { - $dp[self::ITEMS][$k] = $this->namespace . md5(is_scalar($item) ? $item : serialize($item)); - } - } - - // convert CONSTS into CALLBACKS - if (isset($dp[self::CONSTS])) { - foreach ((array) $dp[self::CONSTS] as $item) { - $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item)); - } - unset($dp[self::CONSTS]); - } - - if ($data instanceof Nette\Callback || $data instanceof \Closure) { - Nette\Utils\CriticalSection::enter(); - $data = $data->__invoke(); - Nette\Utils\CriticalSection::leave(); - } - - if (is_object($data)) { - $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data), - Nette\Reflection\ClassType::from($data)->getAnnotation('serializationVersion')); - } - - $this->data = $data; - if ($data === NULL) { - $this->storage->remove($key); - } else { - $this->storage->write($key, $data, (array) $dp); - } - return $data; - } - - - - /** - * Removes items from the cache by conditions. - * Conditions are: - * - Cache::PRIORITY => (int) priority - * - Cache::TAGS => (array) tags - * - Cache::ALL => TRUE - * - * @param array - * @return void - */ - public function clean(array $conds = NULL) - { - $this->release(); - $this->storage->clean((array) $conds); - } - - - - /** - * Caches results of function/method calls. - * @param mixed - * @return mixed - */ - public function call($function) - { - $key = func_get_args(); - if ($this->load($key) === NULL) { - array_shift($key); - return $this->save($this->key, call_user_func_array($function, $key)); - } else { - return $this->data; - } - } - - - - /** - * Starts the output cache. - * @param mixed key - * @return OutputHelper|NULL - */ - public function start($key) - { - if ($this->offsetGet($key) === NULL) { - return new OutputHelper($this, $key); - } else { - echo $this->data; - } - } - - - - /********************* interface ArrayAccess ****************d*g**/ - - - - /** - * Inserts (replaces) item into the cache (\ArrayAccess implementation). - * @param mixed key - * @param mixed - * @return void - * @throws Nette\InvalidArgumentException - */ - public function offsetSet($key, $data) - { - $this->save($key, $data); - } - - - - /** - * Retrieves the specified item from the cache or NULL if the key is not found (\ArrayAccess implementation). - * @param mixed key - * @return mixed|NULL - * @throws Nette\InvalidArgumentException - */ - public function offsetGet($key) - { - return $this->load($key); - } - - - - /** - * Exists item in cache? (\ArrayAccess implementation). - * @param mixed key - * @return bool - * @throws Nette\InvalidArgumentException - */ - public function offsetExists($key) - { - return $this->load($key) !== NULL; - } - - - - /** - * Removes the specified item from the cache. - * @param mixed key - * @return void - * @throws Nette\InvalidArgumentException - */ - public function offsetUnset($key) - { - $this->save($key, NULL); - } - - - - /********************* dependency checkers ****************d*g**/ - - - - /** - * Checks CALLBACKS dependencies. - * @param array - * @return bool - */ - public static function checkCallbacks($callbacks) - { - foreach ($callbacks as $callback) { - $func = array_shift($callback); - if (!call_user_func_array($func, $callback)) { - return FALSE; - } - } - return TRUE; - } - - - - /** - * Checks CONSTS dependency. - * @param string - * @param mixed - * @return bool - */ - private static function checkConst($const, $value) - { - return defined($const) && constant($const) === $value; - } - - - - /** - * Checks FILES dependency. - * @param string - * @param int - * @return bool - */ - private static function checkFile($file, $time) - { - return @filemtime($file) == $time; // @ - stat may fail - } - - - - /** - * Checks object @serializationVersion label. - * @param string - * @param mixed - * @return bool - */ - private static function checkSerializationVersion($class, $value) - { - return Nette\Reflection\ClassType::from($class)->getAnnotation('serializationVersion') === $value; - } - -} +storage = $storage; + $this->namespace = $namespace . self::NAMESPACE_SEPARATOR; + } + + + + /** + * Returns cache storage. + * @return IStorage + */ + public function getStorage() + { + return $this->storage; + } + + + + /** + * Returns cache namespace. + * @return string + */ + public function getNamespace() + { + return (string) substr($this->namespace, 0, -1); + } + + + + /** + * Returns new nested cache object. + * @param string + * @return Cache + */ + public function derive($namespace) + { + $derived = new static($this->storage, $this->namespace . $namespace); + return $derived; + } + + + + /** + * Reads the specified item from the cache or generate it. + * @param mixed key + * @param callable + * @return mixed|NULL + */ + public function load($key, $fallback = NULL) + { + $data = $this->storage->read($this->generateKey($key)); + if ($data === NULL && $fallback) { + return $this->save($key, new Nette\Callback($fallback)); + } + return $data; + } + + + + /** + * Writes item into the cache. + * Dependencies are: + * - Cache::PRIORITY => (int) priority + * - Cache::EXPIRATION => (timestamp) expiration + * - Cache::SLIDING => (bool) use sliding expiration? + * - Cache::TAGS => (array) tags + * - Cache::FILES => (array|string) file names + * - Cache::ITEMS => (array|string) cache items + * - Cache::CONSTS => (array|string) cache items + * + * @param mixed key + * @param mixed value + * @param array dependencies + * @return mixed value itself + * @throws Nette\InvalidArgumentException + */ + public function save($key, $data, array $dp = NULL) + { + $this->release(); + $key = $this->generateKey($key); + + if ($data instanceof Nette\Callback || $data instanceof \Closure) { + $this->storage->lock($key); + $data = Nette\Callback::create($data)->invokeArgs(array(&$dp)); + } + + if ($data === NULL) { + $this->storage->remove($key); + } else { + $this->storage->write($key, $data, $this->completeDependencies($dp, $data)); + return $data; + } + } + + + + private function completeDependencies($dp, $data) + { + if (is_object($data)) { + $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data), + Nette\Reflection\ClassType::from($data)->getAnnotation('serializationVersion')); + } + + // convert expire into relative amount of seconds + if (isset($dp[Cache::EXPIRATION])) { + $dp[Cache::EXPIRATION] = Nette\DateTime::from($dp[Cache::EXPIRATION])->format('U') - time(); + } + + // convert FILES into CALLBACKS + if (isset($dp[self::FILES])) { + //clearstatcache(); + foreach (array_unique((array) $dp[self::FILES]) as $item) { + $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); // @ - stat may fail + } + unset($dp[self::FILES]); + } + + // add namespaces to items + if (isset($dp[self::ITEMS])) { + $dp[self::ITEMS] = array_unique((array) $dp[self::ITEMS]); + foreach ($dp[self::ITEMS] as $k => $item) { + $dp[self::ITEMS][$k] = $this->generateKey($item); + } + } + + // convert CONSTS into CALLBACKS + if (isset($dp[self::CONSTS])) { + foreach (array_unique((array) $dp[self::CONSTS]) as $item) { + $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item)); + } + unset($dp[self::CONSTS]); + } + + if (!is_array($dp)) { + $dp = array(); + } + return $dp; + } + + + + /** + * Removes item from the cache. + * @param mixed key + * @return void + */ + public function remove($key) + { + $this->save($key, NULL); + } + + + + /** + * Removes items from the cache by conditions. + * Conditions are: + * - Cache::PRIORITY => (int) priority + * - Cache::TAGS => (array) tags + * - Cache::ALL => TRUE + * + * @param array + * @return void + */ + public function clean(array $conds = NULL) + { + $this->release(); + $this->storage->clean((array) $conds); + } + + + + /** + * Caches results of function/method calls. + * @param mixed + * @return mixed + */ + public function call($function) + { + $key = func_get_args(); + return $this->load($key, function() use ($function, $key) { + array_shift($key); + return Nette\Callback::create($function)->invokeArgs($key); + }); + } + + + + /** + * Caches results of function/method calls. + * @param mixed + * @param array dependencies + * @return Closure + */ + public function wrap($function, array $dp = NULL) + { + $cache = $this; + return function() use ($cache, $function, $dp) { + $key = array($function, func_get_args()); + $data = $cache->load($key); + if ($data === NULL) { + $data = $cache->save($key, Nette\Callback::create($function)->invokeArgs($key[1]), $dp); + } + return $data; + }; + } + + + + /** + * Starts the output cache. + * @param mixed key + * @return OutputHelper|NULL + */ + public function start($key) + { + $data = $this->load($key); + if ($data === NULL) { + return new OutputHelper($this, $key); + } + echo $data; + } + + + + /** + * Generates internal cache key. + * + * @param string + * @return string + */ + protected function generateKey($key) + { + return $this->namespace . md5(is_scalar($key) ? $key : serialize($key)); + } + + + + /********************* interface ArrayAccess ****************d*g**/ + + + + /** + * Inserts (replaces) item into the cache (\ArrayAccess implementation). + * @param mixed key + * @param mixed + * @return void + * @throws Nette\InvalidArgumentException + */ + public function offsetSet($key, $data) + { + $this->save($key, $data); + } + + + + /** + * Retrieves the specified item from the cache or NULL if the key is not found (\ArrayAccess implementation). + * @param mixed key + * @return mixed|NULL + * @throws Nette\InvalidArgumentException + */ + public function offsetGet($key) + { + $key = is_scalar($key) ? (string) $key : serialize($key); + if ($this->key !== $key) { + $this->key = $key; + $this->data = $this->load($key); + } + return $this->data; + } + + + + /** + * Exists item in cache? (\ArrayAccess implementation). + * @param mixed key + * @return bool + * @throws Nette\InvalidArgumentException + */ + public function offsetExists($key) + { + $this->release(); + return $this->offsetGet($key) !== NULL; + } + + + + /** + * Removes the specified item from the cache. + * @param mixed key + * @return void + * @throws Nette\InvalidArgumentException + */ + public function offsetUnset($key) + { + $this->save($key, NULL); + } + + + + /** + * Discards the internal cache used by ArrayAccess. + * @return void + */ + public function release() + { + $this->key = $this->data = NULL; + } + + + + /********************* dependency checkers ****************d*g**/ + + + + /** + * Checks CALLBACKS dependencies. + * @param array + * @return bool + */ + public static function checkCallbacks($callbacks) + { + foreach ($callbacks as $callback) { + $func = array_shift($callback); + if (!call_user_func_array($func, $callback)) { + return FALSE; + } + } + return TRUE; + } + + + + /** + * Checks CONSTS dependency. + * @param string + * @param mixed + * @return bool + */ + private static function checkConst($const, $value) + { + return defined($const) && constant($const) === $value; + } + + + + /** + * Checks FILES dependency. + * @param string + * @param int + * @return bool + */ + private static function checkFile($file, $time) + { + return @filemtime($file) == $time; // @ - stat may fail + } + + + + /** + * Checks object @serializationVersion label. + * @param string + * @param mixed + * @return bool + */ + private static function checkSerializationVersion($class, $value) + { + return Nette\Reflection\ClassType::from($class)->getAnnotation('serializationVersion') === $value; + } + +} diff --git a/libs/Nette/Caching/IStorage.php b/libs/Nette/Caching/IStorage.php index e01b0ae..cf67751 100644 --- a/libs/Nette/Caching/IStorage.php +++ b/libs/Nette/Caching/IStorage.php @@ -1,56 +1,63 @@ -cache = $cache; - $this->key = $key; - ob_start(); - } - - - - /** - * Stops and saves the cache. - * @return void - */ - public function end() - { - if ($this->cache === NULL) { - throw new Nette\InvalidStateException('Output cache has already been saved.'); - } - $this->cache->save($this->key, ob_get_flush(), $this->dependencies); - $this->cache = NULL; - } - -} +cache = $cache; + $this->key = $key; + ob_start(); + } + + + + /** + * Stops and saves the cache. + * @param array dependencies + * @return void + */ + public function end(array $dp = NULL) + { + if ($this->cache === NULL) { + throw new Nette\InvalidStateException('Output cache has already been saved.'); + } + $this->cache->save($this->key, ob_get_flush(), (array) $dp + (array) $this->dependencies); + $this->cache = NULL; + } + +} diff --git a/libs/Nette/Caching/Storages/DevNullStorage.php b/libs/Nette/Caching/Storages/DevNullStorage.php index f26cbaf..1d6dfd1 100644 --- a/libs/Nette/Caching/Storages/DevNullStorage.php +++ b/libs/Nette/Caching/Storages/DevNullStorage.php @@ -1,70 +1,81 @@ - 0, - self::PRIORITY => 1, - self::ENTRIES => 2, - self::DATA => 3, - ); - - - - /** - * @param string Directory location with journal file - * @return void - */ - public function __construct($dir) - { - $this->file = $dir . '/' . self::FILE; - - // Create jorunal file when not exists - if (!file_exists($this->file)) { - $init = @fopen($this->file, 'xb'); // intentionally @ - if (!$init) { - clearstatcache(); - if (!file_exists($this->file)) { - throw new Nette\InvalidStateException("Cannot create journal file $this->file."); - } - } else { - $writen = fwrite($init, pack('N2', self::FILE_MAGIC, $this->lastNode)); - fclose($init); - if ($writen !== self::INT32_SIZE * 2) { - throw new Nette\InvalidStateException("Cannot write journal header."); - } - } - } - - $this->handle = fopen($this->file, 'r+b'); - - if (!$this->handle) { - throw new Nette\InvalidStateException("Cannot open journal file '$this->file'."); - } - - if (!flock($this->handle, LOCK_SH)) { - throw new Nette\InvalidStateException('Cannot acquire shared lock on journal.'); - } - - $header = stream_get_contents($this->handle, 2 * self::INT32_SIZE, 0); - - flock($this->handle, LOCK_UN); - - list(, $fileMagic, $this->lastNode) = unpack('N2', $header); - - if ($fileMagic !== self::FILE_MAGIC) { - fclose($this->handle); - $this->handle = false; - throw new Nette\InvalidStateException("Malformed journal file '$this->file'."); - } - } - - - - /** - * @return void - */ - public function __destruct() - { - if ($this->handle) { - $this->headerCommit(); - flock($this->handle, LOCK_UN); // Since PHP 5.3.3 is manual unlock necesary - fclose($this->handle); - $this->handle = false; - } - } - - - - /** - * Writes entry information into the journal. - * @param string - * @param array - * @return void - */ - public function write($key, array $dependencies) - { - $this->lock(); - - $priority = !isset($dependencies[Cache::PRIORITY]) ? FALSE : (int) $dependencies[Cache::PRIORITY]; - $tags = empty($dependencies[Cache::TAGS]) ? FALSE : (array) $dependencies[Cache::TAGS]; - - $exists = FALSE; - $keyHash = crc32($key); - list($entriesNodeId, $entriesNode) = $this->findIndexNode(self::ENTRIES, $keyHash); - - if (isset($entriesNode[$keyHash])) { - $entries = $this->mergeIndexData($entriesNode[$keyHash]); - foreach ($entries as $link => $foo) { - $dataNode = $this->getNode($link >> self::BITROT); - if ($dataNode[$link][self::KEY] === $key) { - if ($dataNode[$link][self::TAGS] == $tags && $dataNode[$link][self::PRIORITY] === $priority) { // intentionally ==, the order of tags does not matter - if ($dataNode[$link][self::DELETED]) { - $dataNode[$link][self::DELETED] = FALSE; - $this->saveNode($link >> self::BITROT, $dataNode); - } - $exists = TRUE; - } else { // Alredy exists, but with other tags or priority - $toDelete = array(); - foreach ($dataNode[$link][self::TAGS] as $tag) { - $toDelete[self::TAGS][$tag][$link] = TRUE; - } - if ($dataNode[$link][self::PRIORITY] !== FALSE) { - $toDelete[self::PRIORITY][$dataNode[$link][self::PRIORITY]][$link] = TRUE; - } - $toDelete[self::ENTRIES][$keyHash][$link] = TRUE; - $this->cleanFromIndex($toDelete); - $entriesNode = $this->getNode($entriesNodeId); // Node was changed, get again - unset($dataNode[$link]); - $this->saveNode($link >> self::BITROT, $dataNode); - } - break; - } - } - } - - if ($exists === FALSE) { - // Magical constants - $requiredSize = strlen($key) + 75; - if ($tags) { - foreach ($tags as $tag) { - $requiredSize += strlen($tag) + 13; - } - } - $requiredSize += $priority ? 10 : 1; - - $freeDataNode = $this->findFreeDataNode($requiredSize); - $data = $this->getNode($freeDataNode); - - if ($data === FALSE) { - $data = array( - self::INFO => array( - self::LAST_INDEX => ($freeDataNode << self::BITROT), - self::TYPE => self::DATA, - ) - ); - } - - $dataNodeKey = ++$data[self::INFO][self::LAST_INDEX]; - $data[$dataNodeKey] = array( - self::KEY => $key, - self::TAGS => $tags ? $tags : array(), - self::PRIORITY => $priority, - self::DELETED => FALSE, - ); - - $this->saveNode($freeDataNode, $data); - - // Save to entries tree, ... - $entriesNode[$keyHash][$dataNodeKey] = 1; - $this->saveNode($entriesNodeId, $entriesNode); - - // ...tags tree... - if ($tags) { - foreach ($tags as $tag) { - list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag); - $node[$tag][$dataNodeKey] = 1; - $this->saveNode($nodeId, $node); - } - } - - // ...and priority tree. - if ($priority) { - list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority); - $node[$priority][$dataNodeKey] = 1; - $this->saveNode($nodeId, $node); - } - } - - $this->commit(); - $this->unlock(); - } - - - - /** - * Cleans entries from journal. - * @param array - * @return array of removed items or NULL when performing a full cleanup - */ - public function clean(array $conditions) - { - $this->lock(); - - if (!empty($conditions[Cache::ALL])) { - $this->nodeCache = $this->nodeChanged = $this->dataNodeFreeSpace = array(); - $this->deleteAll(); - $this->unlock(); - return; - } - - $toDelete = array( - self::TAGS => array(), - self::PRIORITY => array(), - self::ENTRIES => array() - ); - - $entries = array(); - - if (!empty($conditions[Cache::TAGS])) { - $entries = $this->cleanTags((array) $conditions[Cache::TAGS], $toDelete); - } - - if (isset($conditions[Cache::PRIORITY])) { - $this->arrayAppend($entries, $this->cleanPriority((int) $conditions[Cache::PRIORITY], $toDelete)); - } - - $this->deletedLinks = array(); - $this->cleanFromIndex($toDelete); - - $this->commit(); - $this->unlock(); - - return $entries; - } - - - - /** - * Cleans entries from journal by tags. - * @param array - * @return array of removed items - */ - private function cleanTags(array $tags, array &$toDelete) - { - $entries = array(); - - foreach ($tags as $tag) { - list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag); - - if (isset($node[$tag])) { - $ent = $this->cleanLinks($this->mergeIndexData($node[$tag]), $toDelete); - $this->arrayAppend($entries, $ent); - } - } - - return $entries; - } - - - - /** - * Cleans entries from journal by priority. - * @param integer - * @param array - * @return array of removed items - */ - private function cleanPriority($priority, array &$toDelete) - { - list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority); - - ksort($node); - - $allData = array(); - - foreach ($node as $prior => $data) { - if ($prior === self::INFO) { - continue; - } elseif ($prior > $priority) { - break; - } - - $this->arrayAppendKeys($allData, $this->mergeIndexData($data)); - } - - $nodeInfo = $node[self::INFO]; - while ($nodeInfo[self::PREV_NODE] !== -1) { - $nodeId = $nodeInfo[self::PREV_NODE]; - $node = $this->getNode($nodeId); - - if ($node === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot load node number $nodeId."); - } - break; - } - - $nodeInfo = $node[self::INFO]; - unset($node[self::INFO]); - - foreach ($node as $prior => $data) { - $this->arrayAppendKeys($allData, $this->mergeIndexData($data)); - } - } - - return $this->cleanLinks($allData, $toDelete); - } - - - - /** - * Cleans links from $data. - * @param array - * @param array - * @return array of removed items - */ - private function cleanLinks(array $data, array &$toDelete) - { - $return = array(); - - $data = array_keys($data); - sort($data); - $max = count($data); - $data[] = 0; - $i = 0; - - while ($i < $max) { - $searchLink = $data[$i]; - - if (isset($this->deletedLinks[$searchLink])) { - ++$i; - continue; - } - - $nodeId = $searchLink >> self::BITROT; - $node = $this->getNode($nodeId); - - if ($node === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException('Cannot load node number ' . ($nodeId) . '.'); - } - ++$i; - continue; - } - - do { - $link = $data[$i]; - - if (!isset($node[$link])) { - if (self::$debug) { - throw new Nette\InvalidStateException("Link with ID $searchLink is not in node ". ($nodeId) . '.'); - } - continue; - } elseif (isset($this->deletedLinks[$link])) { - continue; - } - - $nodeLink = &$node[$link]; - if (!$nodeLink[self::DELETED]) { - $nodeLink[self::DELETED] = TRUE; - $return[] = $nodeLink[self::KEY]; - } else { - foreach ($nodeLink[self::TAGS] as $tag) { - $toDelete[self::TAGS][$tag][$link] = TRUE; - } - if ($nodeLink[self::PRIORITY] !== FALSE) { - $toDelete[self::PRIORITY][$nodeLink[self::PRIORITY]][$link] = TRUE; - } - $toDelete[self::ENTRIES][crc32($nodeLink[self::KEY])][$link] = TRUE; - unset($node[$link]); - $this->deletedLinks[$link] = TRUE; - } - } while (($data[++$i] >> self::BITROT) === $nodeId); - - $this->saveNode($nodeId, $node); - } - - return $return; - } - - - - /** - * Remove links to deleted keys from index. - * @param array - */ - private function cleanFromIndex(array $toDeleteFromIndex) - { - foreach ($toDeleteFromIndex as $type => $toDelete) { - ksort($toDelete); - - while (!empty($toDelete)) { - reset($toDelete); - $searchKey = key($toDelete); - list($masterNodeId, $masterNode) = $this->findIndexNode($type, $searchKey); - - if (!isset($masterNode[$searchKey])) { - if (self::$debug) { - throw new Nette\InvalidStateException('Bad index.'); - } - unset($toDelete[$searchKey]); - continue; - } - - foreach ($toDelete as $key => $links) { - if (isset($masterNode[$key])) { - foreach ($links as $link => $foo) { - if (isset($masterNode[$key][$link])) { - unset($masterNode[$key][$link], $links[$link]); - } - } - - if (!empty($links) && isset($masterNode[$key][self::INDEX_DATA])) { - $this->cleanIndexData($masterNode[$key][self::INDEX_DATA], $links, $masterNode[$key]); - } - - if (empty($masterNode[$key])) { - unset($masterNode[$key]); - } - unset($toDelete[$key]); - } else { - break; - } - } - $this->saveNode($masterNodeId, $masterNode); - } - } - } - - - - /** - * Merge data with index data in other nodes. - * @param array - * @return array of merged items - */ - private function mergeIndexData(array $data) - { - while (isset($data[self::INDEX_DATA])) { - $id = $data[self::INDEX_DATA]; - unset($data[self::INDEX_DATA]); - $childNode = $this->getNode($id); - - if ($childNode === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot load node number $id."); - } - break; - } - - $this->arrayAppendKeys($data, $childNode[self::INDEX_DATA]); - } - - return $data; - } - - - - /** - * Cleans links from other nodes. - * @param int - * @param array - * @param array - * @return void - */ - private function cleanIndexData($nextNodeId, array $links, &$masterNodeLink) - { - $prev = -1; - - while ($nextNodeId && !empty($links)) { - $nodeId = $nextNodeId; - $node = $this->getNode($nodeId); - - if ($node === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot load node number $nodeId."); - } - break; - } - - foreach ($links as $link => $foo) { - if (isset($node[self::INDEX_DATA][$link])) { - unset($node[self::INDEX_DATA][$link], $links[$link]); - } - } - - if (isset($node[self::INDEX_DATA][self::INDEX_DATA])) { - $nextNodeId = $node[self::INDEX_DATA][self::INDEX_DATA]; - } else { - $nextNodeId = FALSE; - } - - if (empty($node[self::INDEX_DATA]) || (count($node[self::INDEX_DATA]) === 1 && $nextNodeId)) { - if ($prev === -1) { - if ($nextNodeId === FALSE) { - unset($masterNodeLink[self::INDEX_DATA]); - } else { - $masterNodeLink[self::INDEX_DATA] = $nextNodeId; - } - } else { - $prevNode = $this->getNode($prev); - if ($prevNode === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot load node number $prev."); - } - } else { - if ($nextNodeId === FALSE) { - unset($prevNode[self::INDEX_DATA][self::INDEX_DATA]); - if (empty($prevNode[self::INDEX_DATA])) { - unset($prevNode[self::INDEX_DATA]); - } - } else { - $prevNode[self::INDEX_DATA][self::INDEX_DATA] = $nextNodeId; - } - - $this->saveNode($prev, $prevNode); - } - } - unset($node[self::INDEX_DATA]); - } else { - $prev = $nodeId; - } - - $this->saveNode($nodeId, $node); - } - } - - - - /** - * Get node from journal. - * @param integer - * @return array - */ - private function getNode($id) - { - // Load from cache - if (isset($this->nodeCache[$id])) { - return $this->nodeCache[$id]; - } - - $binary = stream_get_contents($this->handle, self::NODE_SIZE, self::HEADER_SIZE + self::NODE_SIZE * $id); - - if (empty($binary)) { - // empty node, no Exception - return FALSE; - } - - list(, $magic, $lenght) = unpack('N2', $binary); - if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) { - if (!empty($magic)) { - if (self::$debug) { - throw new Nette\InvalidStateException("Node $id has malformed header."); - } - $this->deleteNode($id); - } - return FALSE; - } - - $data = substr($binary, 2 * self::INT32_SIZE, $lenght - 2 * self::INT32_SIZE); - - $node = @unserialize($data); // intentionally @ - if ($node === FALSE) { - $this->deleteNode($id); - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot deserialize node number $id."); - } - return FALSE; - } - - // Save to cache and return - return $this->nodeCache[$id] = $node; - } - - - - /** - * Save node to cache. - * @param integer - * @param array - * @return void - */ - private function saveNode($id, array $node) - { - if (count($node) === 1) { // Nod contains only INFO - $nodeInfo = $node[self::INFO]; - if ($nodeInfo[self::TYPE] !== self::DATA) { - - if ($nodeInfo[self::END] !== -1) { - $this->nodeCache[$id] = $node; - $this->nodeChanged[$id] = TRUE; - return; - } - - if ($nodeInfo[self::MAX] === -1) { - $max = PHP_INT_MAX; - } else { - $max = $nodeInfo[self::MAX]; - } - - list(, , $parentId) = $this->findIndexNode($nodeInfo[self::TYPE], $max, $id); - if ($parentId !== -1 && $parentId !== $id) { - $parentNode = $this->getNode($parentId); - if ($parentNode === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot load node number $parentId."); - } - } else { - if ($parentNode[self::INFO][self::END] === $id) { - if (count($parentNode) === 1) { - $parentNode[self::INFO][self::END] = -1; - } else { - end($parentNode); - $lastKey = key($parentNode); - $parentNode[self::INFO][self::END] = $parentNode[$lastKey]; - unset($parentNode[$lastKey]); - } - } else { - unset($parentNode[$nodeInfo[self::MAX]]); - } - - $this->saveNode($parentId, $parentNode); - } - } - - if ($nodeInfo[self::TYPE] === self::PRIORITY) { // only priority tree has link to prevNode - if ($nodeInfo[self::MAX] === -1) { - if ($nodeInfo[self::PREV_NODE] !== -1) { - $prevNode = $this->getNode($nodeInfo[self::PREV_NODE]); - if ($prevNode === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException('Cannot load node number ' . $nodeInfo[self::PREV_NODE] . '.'); - } - } else { - $prevNode[self::INFO][self::MAX] = -1; - $this->saveNode($nodeInfo[self::PREV_NODE], $prevNode); - } - } - } else { - list($nextId, $nextNode) = $this->findIndexNode($nodeInfo[self::TYPE], $nodeInfo[self::MAX] + 1, NULL, $id); - if ($nextId !== -1 && $nextId !== $id) { - $nextNode[self::INFO][self::PREV_NODE] = $nodeInfo[self::PREV_NODE]; - $this->saveNode($nextId, $nextNode); - } - } - } - } - $this->nodeCache[$id] = FALSE; - } else { - $this->nodeCache[$id] = $node; - } - $this->nodeChanged[$id] = TRUE; - } - - - - /** - * Commit all changed nodes from cache to journal file. - * @return void - */ - private function commit() - { - do { - foreach ($this->nodeChanged as $id => $foo) { - if ($this->prepareNode($id, $this->nodeCache[$id])) { - unset($this->nodeChanged[$id]); - } - } - } while (!empty($this->nodeChanged)); - - foreach ($this->toCommit as $node => $str) { - $this->commitNode($node, $str); - } - $this->toCommit = array(); - } - - - - /** - * Prepare node to journal file structure. - * @param integer - * @param array|bool - * @return bool Sucessfully commited - */ - private function prepareNode($id, $node) - { - if ($node === FALSE) { - if ($id < $this->lastNode) { - $this->lastNode = $id; - } - unset($this->nodeCache[$id]); - unset($this->dataNodeFreeSpace[$id]); - $this->deleteNode($id); - return TRUE; - } - - $data = serialize($node); - $dataSize = strlen($data) + 2 * self::INT32_SIZE; - - $isData = $node[self::INFO][self::TYPE] === self::DATA; - if ($dataSize > self::NODE_SIZE) { - if ($isData) { - throw new Nette\InvalidStateException('Saving node is bigger than maximum node size.'); - } else { - $this->bisectNode($id, $node); - return FALSE; - } - } - - $this->toCommit[$id] = pack('N2', $isData ? self::DATA_MAGIC : self::INDEX_MAGIC, $dataSize) . $data; - - if ($this->lastNode < $id) { - $this->lastNode = $id; - } - if ($isData) { - $this->dataNodeFreeSpace[$id] = self::NODE_SIZE - $dataSize; - } - - return TRUE; - } - - - - /** - * Commit node string to journal file. - * @param integer - * @param string - * @return void - */ - private function commitNode($id, $str) - { - fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id); - $writen = fwrite($this->handle, $str); - if ($writen === FALSE) { - throw new Nette\InvalidStateException("Cannot write node number $id to journal."); - } - } - - - - /** - * Find right node in B+tree. . - * @param string Tree type (TAGS, PRIORITY or ENTRIES) - * @param int Searched item - * @return array Node - */ - private function findIndexNode($type, $search, $childId = NULL, $prevId = NULL) - { - $nodeId = self::$startNode[$type]; - - $parentId = -1; - while (TRUE) { - $node = $this->getNode($nodeId); - - if ($node === FALSE) { - return array( - $nodeId, - array( - self::INFO => array( - self::TYPE => $type, - self::IS_LEAF => TRUE, - self::PREV_NODE => -1, - self::END => -1, - self::MAX => -1, - ) - ), - $parentId, - ); // Init empty node - } - - if ($node[self::INFO][self::IS_LEAF] || $nodeId === $childId || $node[self::INFO][self::PREV_NODE] === $prevId) { - return array($nodeId, $node, $parentId); - } - - $parentId = $nodeId; - - if (isset($node[$search])) { - $nodeId = $node[$search]; - } else { - foreach ($node as $key => $childNode) { - if ($key > $search and $key !== self::INFO) { - $nodeId = $childNode; - continue 2; - } - } - - $nodeId = $node[self::INFO][self::END]; - } - } - } - - - - /** - * Find complete free node. - * @param integer - * @return array|integer Node ID - */ - private function findFreeNode($count = 1) - { - $id = $this->lastNode; - $nodesId = array(); - - do { - if (isset($this->nodeCache[$id])) { - ++$id; - continue; - } - - $offset = self::HEADER_SIZE + self::NODE_SIZE * $id; - - $binary = stream_get_contents($this->handle, self::INT32_SIZE, $offset); - - if (empty($binary)) { - $nodesId[] = $id; - } else { - list(, $magic) = unpack('N', $binary); - if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) { - $nodesId[] = $id; - } - } - - ++$id; - } while (count($nodesId) !== $count); - - if ($count === 1) { - return $nodesId[0]; - } else { - return $nodesId; - } - } - - - - /** - * Find free data node that has $size bytes of free space. - * @param integer size in bytes - * @return integer Node ID - */ - private function findFreeDataNode($size) - { - foreach ($this->dataNodeFreeSpace as $id => $freeSpace) { - if ($freeSpace > $size) { - return $id; - } - } - - $id = self::$startNode[self::DATA]; - while (TRUE) { - if (isset($this->dataNodeFreeSpace[$id]) || isset($this->nodeCache[$id])) { - ++$id; - continue; - } - - $offset = self::HEADER_SIZE + self::NODE_SIZE * $id; - $binary = stream_get_contents($this->handle, 2 * self::INT32_SIZE, $offset); - - if (empty($binary)) { - $this->dataNodeFreeSpace[$id] = self::NODE_SIZE; - return $id; - } - - list(, $magic, $nodeSize) = unpack('N2', $binary); - if (empty($magic)) { - $this->dataNodeFreeSpace[$id] = self::NODE_SIZE; - return $id; - } elseif ($magic === self::DATA_MAGIC) { - $freeSpace = self::NODE_SIZE - $nodeSize; - $this->dataNodeFreeSpace[$id] = $freeSpace; - - if ($freeSpace > $size) { - return $id; - } - } - - ++$id; - } - } - - - - /** - * Bisect node or when has only one key, move part to data node. - * @param integer Node ID - * @param array Node - * @return void - */ - private function bisectNode($id, array $node) - { - $nodeInfo = $node[self::INFO]; - unset($node[self::INFO]); - - if (count($node) === 1) { - $key = key($node); - - $dataId = $this->findFreeDataNode(self::NODE_SIZE); - $this->saveNode($dataId, array( - self::INDEX_DATA => $node[$key], - self::INFO => array( - self::TYPE => self::DATA, - self::LAST_INDEX => ($dataId << self::BITROT), - ))); - - unset($node[$key]); - $node[$key][self::INDEX_DATA] = $dataId; - $node[self::INFO] = $nodeInfo; - - $this->saveNode($id, $node); - return; - } - - ksort($node); - $halfCount = ceil(count($node) / 2); - - list($first, $second) = array_chunk($node, $halfCount, TRUE); - - end($first); - $halfKey = key($first); - - if ($id <= 2) { // Root - list($firstId, $secondId) = $this->findFreeNode(2); - - $first[self::INFO] = array( - self::TYPE => $nodeInfo[self::TYPE], - self::IS_LEAF => $nodeInfo[self::IS_LEAF], - self::PREV_NODE => -1, - self::END => -1, - self::MAX => $halfKey, - ); - $this->saveNode($firstId, $first); - - $second[self::INFO] = array( - self::TYPE => $nodeInfo[self::TYPE], - self::IS_LEAF => $nodeInfo[self::IS_LEAF], - self::PREV_NODE => $firstId, - self::END => $nodeInfo[self::END], - self::MAX => -1, - ); - $this->saveNode($secondId, $second); - - $parentNode = array( - self::INFO => array( - self::TYPE => $nodeInfo[self::TYPE], - self::IS_LEAF => FALSE, - self::PREV_NODE => -1, - self::END => $secondId, - self::MAX => -1, - ), - $halfKey => $firstId, - ); - $this->saveNode($id, $parentNode); - } else { - $firstId = $this->findFreeNode(); - - $first[self::INFO] = array( - self::TYPE => $nodeInfo[self::TYPE], - self::IS_LEAF => $nodeInfo[self::IS_LEAF], - self::PREV_NODE => $nodeInfo[self::PREV_NODE], - self::END => -1, - self::MAX => $halfKey, - ); - $this->saveNode($firstId, $first); - - $second[self::INFO] = array( - self::TYPE => $nodeInfo[self::TYPE], - self::IS_LEAF => $nodeInfo[self::IS_LEAF], - self::PREV_NODE => $firstId, - self::END => $nodeInfo[self::END], - self::MAX => $nodeInfo[self::MAX], - ); - $this->saveNode($id, $second); - - list(,, $parent) = $this->findIndexNode($nodeInfo[self::TYPE], $halfKey); - $parentNode = $this->getNode($parent); - if ($parentNode === FALSE) { - if (self::$debug) { - throw new Nette\InvalidStateException("Cannot load node number $parent."); - } - } else { - $parentNode[$halfKey] = $firstId; - ksort($parentNode); // Parent index must be always sorted. - $this->saveNode($parent, $parentNode); - } - } - } - - - - /** - * Commit header to journal file. - * @return void - */ - private function headerCommit() - { - fseek($this->handle, self::INT32_SIZE); - @fwrite($this->handle, pack('N', $this->lastNode)); // intentionally @, save is not necceseary - } - - - - /** - * Remove node from journal file. - * @param integer - * @return void - */ - private function deleteNode($id) - { - fseek($this->handle, 0, SEEK_END); - $end = ftell($this->handle); - - if ($end <= (self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) { - $packedNull = pack('N', 0); - - do { - $binary = stream_get_contents($this->handle, self::INT32_SIZE, (self::HEADER_SIZE + self::NODE_SIZE * --$id)); - } while (empty($binary) || $binary === $packedNull); - - if (!ftruncate($this->handle, self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) { - throw new Nette\InvalidStateException('Cannot truncate journal file.'); - } - } else { - fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id); - $writen = fwrite($this->handle, pack('N', 0)); - if ($writen !== self::INT32_SIZE) { - throw new Nette\InvalidStateException("Cannot delete node number $id from journal."); - } - } - } - - - - /** - * Complete delete all nodes from file. - * @return void - */ - private function deleteAll() - { - if (!ftruncate($this->handle, self::HEADER_SIZE)) { - throw new Nette\InvalidStateException('Cannot truncate journal file.'); - } - } - - - - /** - * Lock file for writing and reading and delete node cache when file has changed. - * @return void - */ - private function lock() - { - if (!$this->handle) { - throw new Nette\InvalidStateException('File journal file is not opened'); - } - - if (!flock($this->handle, LOCK_EX)) { - throw new Nette\InvalidStateException('Cannot acquire exclusive lock on journal.'); - } - - if ($this->lastModTime !== NULL) { - clearstatcache(); - if ($this->lastModTime < @filemtime($this->file)) { // intentionally @ - $this->nodeCache = $this->dataNodeFreeSpace = array(); - } - } - } - - - - /** - * Unlock file and save last modified time. - * @return void - */ - private function unlock() - { - if ($this->handle) { - fflush($this->handle); - flock($this->handle, LOCK_UN); - clearstatcache(); - $this->lastModTime = @filemtime($this->file); // intentionally @ - } - } - - - - /** - * Append $append to $array. - * This function is much faster then $array = array_merge($array, $append) - * @param array - * @param array - * @return void - */ - private function arrayAppend(array &$array, array $append) - { - foreach ($append as $value) { - $array[] = $value; - } - } - - - - /** - * Append $append to $array with preserve keys. - * This function is much faster then $array = $array + $append - * @param array - * @param array - * @return void - */ - private function arrayAppendKeys(array &$array, array $append) - { - foreach ($append as $key => $value) { - $array[$key] = $value; - } - } - -} + 0, + self::PRIORITY => 1, + self::ENTRIES => 2, + self::DATA => 3, + ); + + + + /** + * @param string Directory location with journal file + */ + public function __construct($dir) + { + $this->file = $dir . '/' . self::FILE; + + // Create jorunal file when not exists + if (!file_exists($this->file)) { + $init = @fopen($this->file, 'xb'); // intentionally @ + if (!$init) { + clearstatcache(); + if (!file_exists($this->file)) { + throw new Nette\InvalidStateException("Cannot create journal file $this->file."); + } + } else { + $writen = fwrite($init, pack('N2', self::FILE_MAGIC, $this->lastNode)); + fclose($init); + if ($writen !== self::INT32_SIZE * 2) { + throw new Nette\InvalidStateException("Cannot write journal header."); + } + } + } + + $this->handle = fopen($this->file, 'r+b'); + + if (!$this->handle) { + throw new Nette\InvalidStateException("Cannot open journal file '$this->file'."); + } + + if (!flock($this->handle, LOCK_SH)) { + throw new Nette\InvalidStateException('Cannot acquire shared lock on journal.'); + } + + $header = stream_get_contents($this->handle, 2 * self::INT32_SIZE, 0); + + flock($this->handle, LOCK_UN); + + list(, $fileMagic, $this->lastNode) = unpack('N2', $header); + + if ($fileMagic !== self::FILE_MAGIC) { + fclose($this->handle); + $this->handle = false; + throw new Nette\InvalidStateException("Malformed journal file '$this->file'."); + } + } + + + + /** + * @return void + */ + public function __destruct() + { + if ($this->handle) { + $this->headerCommit(); + flock($this->handle, LOCK_UN); // Since PHP 5.3.3 is manual unlock necesary + fclose($this->handle); + $this->handle = false; + } + } + + + + /** + * Writes entry information into the journal. + * @param string + * @param array + * @return void + */ + public function write($key, array $dependencies) + { + $this->lock(); + + $priority = !isset($dependencies[Cache::PRIORITY]) ? FALSE : (int) $dependencies[Cache::PRIORITY]; + $tags = empty($dependencies[Cache::TAGS]) ? FALSE : (array) $dependencies[Cache::TAGS]; + + $exists = FALSE; + $keyHash = crc32($key); + list($entriesNodeId, $entriesNode) = $this->findIndexNode(self::ENTRIES, $keyHash); + + if (isset($entriesNode[$keyHash])) { + $entries = $this->mergeIndexData($entriesNode[$keyHash]); + foreach ($entries as $link => $foo) { + $dataNode = $this->getNode($link >> self::BITROT); + if ($dataNode[$link][self::KEY] === $key) { + if ($dataNode[$link][self::TAGS] == $tags && $dataNode[$link][self::PRIORITY] === $priority) { // intentionally ==, the order of tags does not matter + if ($dataNode[$link][self::DELETED]) { + $dataNode[$link][self::DELETED] = FALSE; + $this->saveNode($link >> self::BITROT, $dataNode); + } + $exists = TRUE; + } else { // Alredy exists, but with other tags or priority + $toDelete = array(); + foreach ($dataNode[$link][self::TAGS] as $tag) { + $toDelete[self::TAGS][$tag][$link] = TRUE; + } + if ($dataNode[$link][self::PRIORITY] !== FALSE) { + $toDelete[self::PRIORITY][$dataNode[$link][self::PRIORITY]][$link] = TRUE; + } + $toDelete[self::ENTRIES][$keyHash][$link] = TRUE; + $this->cleanFromIndex($toDelete); + $entriesNode = $this->getNode($entriesNodeId); // Node was changed, get again + unset($dataNode[$link]); + $this->saveNode($link >> self::BITROT, $dataNode); + } + break; + } + } + } + + if ($exists === FALSE) { + // Magical constants + $requiredSize = strlen($key) + 75; + if ($tags) { + foreach ($tags as $tag) { + $requiredSize += strlen($tag) + 13; + } + } + $requiredSize += $priority ? 10 : 1; + + $freeDataNode = $this->findFreeDataNode($requiredSize); + $data = $this->getNode($freeDataNode); + + if ($data === FALSE) { + $data = array( + self::INFO => array( + self::LAST_INDEX => ($freeDataNode << self::BITROT), + self::TYPE => self::DATA, + ) + ); + } + + $dataNodeKey = ++$data[self::INFO][self::LAST_INDEX]; + $data[$dataNodeKey] = array( + self::KEY => $key, + self::TAGS => $tags ? $tags : array(), + self::PRIORITY => $priority, + self::DELETED => FALSE, + ); + + $this->saveNode($freeDataNode, $data); + + // Save to entries tree, ... + $entriesNode[$keyHash][$dataNodeKey] = 1; + $this->saveNode($entriesNodeId, $entriesNode); + + // ...tags tree... + if ($tags) { + foreach ($tags as $tag) { + list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag); + $node[$tag][$dataNodeKey] = 1; + $this->saveNode($nodeId, $node); + } + } + + // ...and priority tree. + if ($priority) { + list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority); + $node[$priority][$dataNodeKey] = 1; + $this->saveNode($nodeId, $node); + } + } + + $this->commit(); + $this->unlock(); + } + + + + /** + * Cleans entries from journal. + * @param array + * @return array of removed items or NULL when performing a full cleanup + */ + public function clean(array $conditions) + { + $this->lock(); + + if (!empty($conditions[Cache::ALL])) { + $this->nodeCache = $this->nodeChanged = $this->dataNodeFreeSpace = array(); + $this->deleteAll(); + $this->unlock(); + return; + } + + $toDelete = array( + self::TAGS => array(), + self::PRIORITY => array(), + self::ENTRIES => array() + ); + + $entries = array(); + + if (!empty($conditions[Cache::TAGS])) { + $entries = $this->cleanTags((array) $conditions[Cache::TAGS], $toDelete); + } + + if (isset($conditions[Cache::PRIORITY])) { + $this->arrayAppend($entries, $this->cleanPriority((int) $conditions[Cache::PRIORITY], $toDelete)); + } + + $this->deletedLinks = array(); + $this->cleanFromIndex($toDelete); + + $this->commit(); + $this->unlock(); + + return $entries; + } + + + + /** + * Cleans entries from journal by tags. + * @return array of removed items + */ + private function cleanTags(array $tags, array &$toDelete) + { + $entries = array(); + + foreach ($tags as $tag) { + list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag); + + if (isset($node[$tag])) { + $ent = $this->cleanLinks($this->mergeIndexData($node[$tag]), $toDelete); + $this->arrayAppend($entries, $ent); + } + } + + return $entries; + } + + + + /** + * Cleans entries from journal by priority. + * @param integer + * @param array + * @return array of removed items + */ + private function cleanPriority($priority, array &$toDelete) + { + list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority); + + ksort($node); + + $allData = array(); + + foreach ($node as $prior => $data) { + if ($prior === self::INFO) { + continue; + } elseif ($prior > $priority) { + break; + } + + $this->arrayAppendKeys($allData, $this->mergeIndexData($data)); + } + + $nodeInfo = $node[self::INFO]; + while ($nodeInfo[self::PREV_NODE] !== -1) { + $nodeId = $nodeInfo[self::PREV_NODE]; + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $nodeId."); + } + break; + } + + $nodeInfo = $node[self::INFO]; + unset($node[self::INFO]); + + foreach ($node as $prior => $data) { + $this->arrayAppendKeys($allData, $this->mergeIndexData($data)); + } + } + + return $this->cleanLinks($allData, $toDelete); + } + + + + /** + * Cleans links from $data. + * @param array + * @param array + * @return array of removed items + */ + private function cleanLinks(array $data, array &$toDelete) + { + $return = array(); + + $data = array_keys($data); + sort($data); + $max = count($data); + $data[] = 0; + $i = 0; + + while ($i < $max) { + $searchLink = $data[$i]; + + if (isset($this->deletedLinks[$searchLink])) { + ++$i; + continue; + } + + $nodeId = $searchLink >> self::BITROT; + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException('Cannot load node number ' . ($nodeId) . '.'); + } + ++$i; + continue; + } + + do { + $link = $data[$i]; + + if (!isset($node[$link])) { + if (self::$debug) { + throw new Nette\InvalidStateException("Link with ID $searchLink is not in node ". ($nodeId) . '.'); + } + continue; + } elseif (isset($this->deletedLinks[$link])) { + continue; + } + + $nodeLink = &$node[$link]; + if (!$nodeLink[self::DELETED]) { + $nodeLink[self::DELETED] = TRUE; + $return[] = $nodeLink[self::KEY]; + } else { + foreach ($nodeLink[self::TAGS] as $tag) { + $toDelete[self::TAGS][$tag][$link] = TRUE; + } + if ($nodeLink[self::PRIORITY] !== FALSE) { + $toDelete[self::PRIORITY][$nodeLink[self::PRIORITY]][$link] = TRUE; + } + $toDelete[self::ENTRIES][crc32($nodeLink[self::KEY])][$link] = TRUE; + unset($node[$link]); + $this->deletedLinks[$link] = TRUE; + } + } while (($data[++$i] >> self::BITROT) === $nodeId); + + $this->saveNode($nodeId, $node); + } + + return $return; + } + + + + /** + * Remove links to deleted keys from index. + * @param array + */ + private function cleanFromIndex(array $toDeleteFromIndex) + { + foreach ($toDeleteFromIndex as $type => $toDelete) { + ksort($toDelete); + + while (!empty($toDelete)) { + reset($toDelete); + $searchKey = key($toDelete); + list($masterNodeId, $masterNode) = $this->findIndexNode($type, $searchKey); + + if (!isset($masterNode[$searchKey])) { + if (self::$debug) { + throw new Nette\InvalidStateException('Bad index.'); + } + unset($toDelete[$searchKey]); + continue; + } + + foreach ($toDelete as $key => $links) { + if (isset($masterNode[$key])) { + foreach ($links as $link => $foo) { + if (isset($masterNode[$key][$link])) { + unset($masterNode[$key][$link], $links[$link]); + } + } + + if (!empty($links) && isset($masterNode[$key][self::INDEX_DATA])) { + $this->cleanIndexData($masterNode[$key][self::INDEX_DATA], $links, $masterNode[$key]); + } + + if (empty($masterNode[$key])) { + unset($masterNode[$key]); + } + unset($toDelete[$key]); + } else { + break; + } + } + $this->saveNode($masterNodeId, $masterNode); + } + } + } + + + + /** + * Merge data with index data in other nodes. + * @param array + * @return array of merged items + */ + private function mergeIndexData(array $data) + { + while (isset($data[self::INDEX_DATA])) { + $id = $data[self::INDEX_DATA]; + unset($data[self::INDEX_DATA]); + $childNode = $this->getNode($id); + + if ($childNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $id."); + } + break; + } + + $this->arrayAppendKeys($data, $childNode[self::INDEX_DATA]); + } + + return $data; + } + + + + /** + * Cleans links from other nodes. + * @param int + * @param array + * @param array + * @return void + */ + private function cleanIndexData($nextNodeId, array $links, &$masterNodeLink) + { + $prev = -1; + + while ($nextNodeId && !empty($links)) { + $nodeId = $nextNodeId; + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $nodeId."); + } + break; + } + + foreach ($links as $link => $foo) { + if (isset($node[self::INDEX_DATA][$link])) { + unset($node[self::INDEX_DATA][$link], $links[$link]); + } + } + + if (isset($node[self::INDEX_DATA][self::INDEX_DATA])) { + $nextNodeId = $node[self::INDEX_DATA][self::INDEX_DATA]; + } else { + $nextNodeId = FALSE; + } + + if (empty($node[self::INDEX_DATA]) || (count($node[self::INDEX_DATA]) === 1 && $nextNodeId)) { + if ($prev === -1) { + if ($nextNodeId === FALSE) { + unset($masterNodeLink[self::INDEX_DATA]); + } else { + $masterNodeLink[self::INDEX_DATA] = $nextNodeId; + } + } else { + $prevNode = $this->getNode($prev); + if ($prevNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $prev."); + } + } else { + if ($nextNodeId === FALSE) { + unset($prevNode[self::INDEX_DATA][self::INDEX_DATA]); + if (empty($prevNode[self::INDEX_DATA])) { + unset($prevNode[self::INDEX_DATA]); + } + } else { + $prevNode[self::INDEX_DATA][self::INDEX_DATA] = $nextNodeId; + } + + $this->saveNode($prev, $prevNode); + } + } + unset($node[self::INDEX_DATA]); + } else { + $prev = $nodeId; + } + + $this->saveNode($nodeId, $node); + } + } + + + + /** + * Get node from journal. + * @param integer + * @return array + */ + private function getNode($id) + { + // Load from cache + if (isset($this->nodeCache[$id])) { + return $this->nodeCache[$id]; + } + + $binary = stream_get_contents($this->handle, self::NODE_SIZE, self::HEADER_SIZE + self::NODE_SIZE * $id); + + if (empty($binary)) { + // empty node, no Exception + return FALSE; + } + + list(, $magic, $lenght) = unpack('N2', $binary); + if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) { + if (!empty($magic)) { + if (self::$debug) { + throw new Nette\InvalidStateException("Node $id has malformed header."); + } + $this->deleteNode($id); + } + return FALSE; + } + + $data = substr($binary, 2 * self::INT32_SIZE, $lenght - 2 * self::INT32_SIZE); + + $node = @unserialize($data); // intentionally @ + if ($node === FALSE) { + $this->deleteNode($id); + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot deserialize node number $id."); + } + return FALSE; + } + + // Save to cache and return + return $this->nodeCache[$id] = $node; + } + + + + /** + * Save node to cache. + * @param integer + * @param array + * @return void + */ + private function saveNode($id, array $node) + { + if (count($node) === 1) { // Nod contains only INFO + $nodeInfo = $node[self::INFO]; + if ($nodeInfo[self::TYPE] !== self::DATA) { + + if ($nodeInfo[self::END] !== -1) { + $this->nodeCache[$id] = $node; + $this->nodeChanged[$id] = TRUE; + return; + } + + if ($nodeInfo[self::MAX] === -1) { + $max = PHP_INT_MAX; + } else { + $max = $nodeInfo[self::MAX]; + } + + list(, , $parentId) = $this->findIndexNode($nodeInfo[self::TYPE], $max, $id); + if ($parentId !== -1 && $parentId !== $id) { + $parentNode = $this->getNode($parentId); + if ($parentNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $parentId."); + } + } else { + if ($parentNode[self::INFO][self::END] === $id) { + if (count($parentNode) === 1) { + $parentNode[self::INFO][self::END] = -1; + } else { + end($parentNode); + $lastKey = key($parentNode); + $parentNode[self::INFO][self::END] = $parentNode[$lastKey]; + unset($parentNode[$lastKey]); + } + } else { + unset($parentNode[$nodeInfo[self::MAX]]); + } + + $this->saveNode($parentId, $parentNode); + } + } + + if ($nodeInfo[self::TYPE] === self::PRIORITY) { // only priority tree has link to prevNode + if ($nodeInfo[self::MAX] === -1) { + if ($nodeInfo[self::PREV_NODE] !== -1) { + $prevNode = $this->getNode($nodeInfo[self::PREV_NODE]); + if ($prevNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException('Cannot load node number ' . $nodeInfo[self::PREV_NODE] . '.'); + } + } else { + $prevNode[self::INFO][self::MAX] = -1; + $this->saveNode($nodeInfo[self::PREV_NODE], $prevNode); + } + } + } else { + list($nextId, $nextNode) = $this->findIndexNode($nodeInfo[self::TYPE], $nodeInfo[self::MAX] + 1, NULL, $id); + if ($nextId !== -1 && $nextId !== $id) { + $nextNode[self::INFO][self::PREV_NODE] = $nodeInfo[self::PREV_NODE]; + $this->saveNode($nextId, $nextNode); + } + } + } + } + $this->nodeCache[$id] = FALSE; + } else { + $this->nodeCache[$id] = $node; + } + $this->nodeChanged[$id] = TRUE; + } + + + + /** + * Commit all changed nodes from cache to journal file. + * @return void + */ + private function commit() + { + do { + foreach ($this->nodeChanged as $id => $foo) { + if ($this->prepareNode($id, $this->nodeCache[$id])) { + unset($this->nodeChanged[$id]); + } + } + } while (!empty($this->nodeChanged)); + + foreach ($this->toCommit as $node => $str) { + $this->commitNode($node, $str); + } + $this->toCommit = array(); + } + + + + /** + * Prepare node to journal file structure. + * @param integer + * @param array|bool + * @return bool Sucessfully commited + */ + private function prepareNode($id, $node) + { + if ($node === FALSE) { + if ($id < $this->lastNode) { + $this->lastNode = $id; + } + unset($this->nodeCache[$id]); + unset($this->dataNodeFreeSpace[$id]); + $this->deleteNode($id); + return TRUE; + } + + $data = serialize($node); + $dataSize = strlen($data) + 2 * self::INT32_SIZE; + + $isData = $node[self::INFO][self::TYPE] === self::DATA; + if ($dataSize > self::NODE_SIZE) { + if ($isData) { + throw new Nette\InvalidStateException('Saving node is bigger than maximum node size.'); + } else { + $this->bisectNode($id, $node); + return FALSE; + } + } + + $this->toCommit[$id] = pack('N2', $isData ? self::DATA_MAGIC : self::INDEX_MAGIC, $dataSize) . $data; + + if ($this->lastNode < $id) { + $this->lastNode = $id; + } + if ($isData) { + $this->dataNodeFreeSpace[$id] = self::NODE_SIZE - $dataSize; + } + + return TRUE; + } + + + + /** + * Commit node string to journal file. + * @param integer + * @param string + * @return void + */ + private function commitNode($id, $str) + { + fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id); + $writen = fwrite($this->handle, $str); + if ($writen === FALSE) { + throw new Nette\InvalidStateException("Cannot write node number $id to journal."); + } + } + + + + /** + * Find right node in B+tree. . + * @param string Tree type (TAGS, PRIORITY or ENTRIES) + * @param int Searched item + * @return array Node + */ + private function findIndexNode($type, $search, $childId = NULL, $prevId = NULL) + { + $nodeId = self::$startNode[$type]; + + $parentId = -1; + while (TRUE) { + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + return array( + $nodeId, + array( + self::INFO => array( + self::TYPE => $type, + self::IS_LEAF => TRUE, + self::PREV_NODE => -1, + self::END => -1, + self::MAX => -1, + ) + ), + $parentId, + ); // Init empty node + } + + if ($node[self::INFO][self::IS_LEAF] || $nodeId === $childId || $node[self::INFO][self::PREV_NODE] === $prevId) { + return array($nodeId, $node, $parentId); + } + + $parentId = $nodeId; + + if (isset($node[$search])) { + $nodeId = $node[$search]; + } else { + foreach ($node as $key => $childNode) { + if ($key > $search and $key !== self::INFO) { + $nodeId = $childNode; + continue 2; + } + } + + $nodeId = $node[self::INFO][self::END]; + } + } + } + + + + /** + * Find complete free node. + * @param integer + * @return array|integer Node ID + */ + private function findFreeNode($count = 1) + { + $id = $this->lastNode; + $nodesId = array(); + + do { + if (isset($this->nodeCache[$id])) { + ++$id; + continue; + } + + $offset = self::HEADER_SIZE + self::NODE_SIZE * $id; + + $binary = stream_get_contents($this->handle, self::INT32_SIZE, $offset); + + if (empty($binary)) { + $nodesId[] = $id; + } else { + list(, $magic) = unpack('N', $binary); + if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) { + $nodesId[] = $id; + } + } + + ++$id; + } while (count($nodesId) !== $count); + + if ($count === 1) { + return $nodesId[0]; + } else { + return $nodesId; + } + } + + + + /** + * Find free data node that has $size bytes of free space. + * @param integer size in bytes + * @return integer Node ID + */ + private function findFreeDataNode($size) + { + foreach ($this->dataNodeFreeSpace as $id => $freeSpace) { + if ($freeSpace > $size) { + return $id; + } + } + + $id = self::$startNode[self::DATA]; + while (TRUE) { + if (isset($this->dataNodeFreeSpace[$id]) || isset($this->nodeCache[$id])) { + ++$id; + continue; + } + + $offset = self::HEADER_SIZE + self::NODE_SIZE * $id; + $binary = stream_get_contents($this->handle, 2 * self::INT32_SIZE, $offset); + + if (empty($binary)) { + $this->dataNodeFreeSpace[$id] = self::NODE_SIZE; + return $id; + } + + list(, $magic, $nodeSize) = unpack('N2', $binary); + if (empty($magic)) { + $this->dataNodeFreeSpace[$id] = self::NODE_SIZE; + return $id; + } elseif ($magic === self::DATA_MAGIC) { + $freeSpace = self::NODE_SIZE - $nodeSize; + $this->dataNodeFreeSpace[$id] = $freeSpace; + + if ($freeSpace > $size) { + return $id; + } + } + + ++$id; + } + } + + + + /** + * Bisect node or when has only one key, move part to data node. + * @param integer Node ID + * @param array Node + * @return void + */ + private function bisectNode($id, array $node) + { + $nodeInfo = $node[self::INFO]; + unset($node[self::INFO]); + + if (count($node) === 1) { + $key = key($node); + + $dataId = $this->findFreeDataNode(self::NODE_SIZE); + $this->saveNode($dataId, array( + self::INDEX_DATA => $node[$key], + self::INFO => array( + self::TYPE => self::DATA, + self::LAST_INDEX => ($dataId << self::BITROT), + ))); + + unset($node[$key]); + $node[$key][self::INDEX_DATA] = $dataId; + $node[self::INFO] = $nodeInfo; + + $this->saveNode($id, $node); + return; + } + + ksort($node); + $halfCount = ceil(count($node) / 2); + + list($first, $second) = array_chunk($node, $halfCount, TRUE); + + end($first); + $halfKey = key($first); + + if ($id <= 2) { // Root + list($firstId, $secondId) = $this->findFreeNode(2); + + $first[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => -1, + self::END => -1, + self::MAX => $halfKey, + ); + $this->saveNode($firstId, $first); + + $second[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => $firstId, + self::END => $nodeInfo[self::END], + self::MAX => -1, + ); + $this->saveNode($secondId, $second); + + $parentNode = array( + self::INFO => array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => FALSE, + self::PREV_NODE => -1, + self::END => $secondId, + self::MAX => -1, + ), + $halfKey => $firstId, + ); + $this->saveNode($id, $parentNode); + } else { + $firstId = $this->findFreeNode(); + + $first[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => $nodeInfo[self::PREV_NODE], + self::END => -1, + self::MAX => $halfKey, + ); + $this->saveNode($firstId, $first); + + $second[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => $firstId, + self::END => $nodeInfo[self::END], + self::MAX => $nodeInfo[self::MAX], + ); + $this->saveNode($id, $second); + + list(,, $parent) = $this->findIndexNode($nodeInfo[self::TYPE], $halfKey); + $parentNode = $this->getNode($parent); + if ($parentNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $parent."); + } + } else { + $parentNode[$halfKey] = $firstId; + ksort($parentNode); // Parent index must be always sorted. + $this->saveNode($parent, $parentNode); + } + } + } + + + + /** + * Commit header to journal file. + * @return void + */ + private function headerCommit() + { + fseek($this->handle, self::INT32_SIZE); + @fwrite($this->handle, pack('N', $this->lastNode)); // intentionally @, save is not necceseary + } + + + + /** + * Remove node from journal file. + * @param integer + * @return void + */ + private function deleteNode($id) + { + fseek($this->handle, 0, SEEK_END); + $end = ftell($this->handle); + + if ($end <= (self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) { + $packedNull = pack('N', 0); + + do { + $binary = stream_get_contents($this->handle, self::INT32_SIZE, (self::HEADER_SIZE + self::NODE_SIZE * --$id)); + } while (empty($binary) || $binary === $packedNull); + + if (!ftruncate($this->handle, self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) { + throw new Nette\InvalidStateException('Cannot truncate journal file.'); + } + } else { + fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id); + $writen = fwrite($this->handle, pack('N', 0)); + if ($writen !== self::INT32_SIZE) { + throw new Nette\InvalidStateException("Cannot delete node number $id from journal."); + } + } + } + + + + /** + * Complete delete all nodes from file. + * @return void + */ + private function deleteAll() + { + if (!ftruncate($this->handle, self::HEADER_SIZE)) { + throw new Nette\InvalidStateException('Cannot truncate journal file.'); + } + } + + + + /** + * Lock file for writing and reading and delete node cache when file has changed. + * @return void + */ + private function lock() + { + if (!$this->handle) { + throw new Nette\InvalidStateException('File journal file is not opened'); + } + + if (!flock($this->handle, LOCK_EX)) { + throw new Nette\InvalidStateException('Cannot acquire exclusive lock on journal.'); + } + + if ($this->lastModTime !== NULL) { + clearstatcache(); + if ($this->lastModTime < @filemtime($this->file)) { // intentionally @ + $this->nodeCache = $this->dataNodeFreeSpace = array(); + } + } + } + + + + /** + * Unlock file and save last modified time. + * @return void + */ + private function unlock() + { + if ($this->handle) { + fflush($this->handle); + flock($this->handle, LOCK_UN); + clearstatcache(); + $this->lastModTime = @filemtime($this->file); // intentionally @ + } + } + + + + /** + * Append $append to $array. + * This function is much faster then $array = array_merge($array, $append) + * @param array + * @param array + * @return void + */ + private function arrayAppend(array &$array, array $append) + { + foreach ($append as $value) { + $array[] = $value; + } + } + + + + /** + * Append $append to $array with preserve keys. + * This function is much faster then $array = $array + $append + * @param array + * @param array + * @return void + */ + private function arrayAppendKeys(array &$array, array $append) + { + foreach ($append as $key => $value) { + $array[$key] = $value; + } + } + +} diff --git a/libs/Nette/Caching/Storages/FileStorage.php b/libs/Nette/Caching/Storages/FileStorage.php index 995d929..915c6a0 100644 --- a/libs/Nette/Caching/Storages/FileStorage.php +++ b/libs/Nette/Caching/Storages/FileStorage.php @@ -1,403 +1,429 @@ - timestamp) - META_CALLBACKS = 'callbacks'; // array of callbacks (function, args) - - /** additional cache structure */ - const FILE = 'file', - HANDLE = 'handle'; - - - /** @var float probability that the clean() routine is started */ - public static $gcProbability = 0.001; - - /** @var bool */ - public static $useDirectories = TRUE; - - /** @var string */ - private $dir; - - /** @var bool */ - private $useDirs; - - /** @var IJournal */ - private $journal; - - - - public function __construct($dir, IJournal $journal = NULL) - { - $this->dir = realpath($dir); - if ($this->dir === FALSE) { - throw new Nette\DirectoryNotFoundException("Directory '$dir' not found."); - } - - $this->useDirs = (bool) self::$useDirectories; - $this->journal = $journal; - - if (mt_rand() / mt_getrandmax() < self::$gcProbability) { - $this->clean(array()); - } - } - - - - /** - * Read from cache. - * @param string key - * @return mixed|NULL - */ - public function read($key) - { - $meta = $this->readMetaAndLock($this->getCacheFile($key), LOCK_SH); - if ($meta && $this->verify($meta)) { - return $this->readData($meta); // calls fclose() - - } else { - return NULL; - } - } - - - - /** - * Verifies dependencies. - * @param array - * @return bool - */ - private function verify($meta) - { - do { - if (!empty($meta[self::META_DELTA])) { - // meta[file] was added by readMetaAndLock() - if (filemtime($meta[self::FILE]) + $meta[self::META_DELTA] < time()) { - break; - } - touch($meta[self::FILE]); - - } elseif (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < time()) { - break; - } - - if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { - break; - } - - if (!empty($meta[self::META_ITEMS])) { - foreach ($meta[self::META_ITEMS] as $depFile => $time) { - $m = $this->readMetaAndLock($depFile, LOCK_SH); - if ($m[self::META_TIME] !== $time || ($m && !$this->verify($m))) { - break 2; - } - } - } - - return TRUE; - } while (FALSE); - - $this->delete($meta[self::FILE], $meta[self::HANDLE]); // meta[handle] & meta[file] was added by readMetaAndLock() - return FALSE; - } - - - - /** - * Writes item into the cache. - * @param string key - * @param mixed data - * @param array dependencies - * @return void - */ - public function write($key, $data, array $dp) - { - $meta = array( - self::META_TIME => microtime(), - ); - - if (isset($dp[Cache::EXPIRATION])) { - if (empty($dp[Cache::SLIDING])) { - $meta[self::META_EXPIRE] = $dp[Cache::EXPIRATION] + time(); // absolute time - } else { - $meta[self::META_DELTA] = (int) $dp[Cache::EXPIRATION]; // sliding time - } - } - - if (isset($dp[Cache::ITEMS])) { - foreach ((array) $dp[Cache::ITEMS] as $item) { - $depFile = $this->getCacheFile($item); - $m = $this->readMetaAndLock($depFile, LOCK_SH); - $meta[self::META_ITEMS][$depFile] = $m[self::META_TIME]; // may be NULL - unset($m); - } - } - - if (isset($dp[Cache::CALLBACKS])) { - $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; - } - - $cacheFile = $this->getCacheFile($key); - if ($this->useDirs && !is_dir($dir = dirname($cacheFile))) { - umask(0000); - if (!mkdir($dir, 0777)) { - return; - } - } - $handle = @fopen($cacheFile, 'r+b'); // @ - file may not exist - if (!$handle) { - $handle = fopen($cacheFile, 'wb'); - if (!$handle) { - return; - } - } - - if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { - if (!$this->journal) { - throw new Nette\InvalidStateException('CacheJournal has not been provided.'); - } - $this->journal->write($cacheFile, $dp); - } - - flock($handle, LOCK_EX); - ftruncate($handle, 0); - - if (!is_string($data)) { - $data = serialize($data); - $meta[self::META_SERIALIZED] = TRUE; - } - - $head = serialize($meta) . '?>'; - $head = 'delete($cacheFile, $handle); - } - - - - /** - * Removes item from the cache. - * @param string key - * @return void - */ - public function remove($key) - { - $this->delete($this->getCacheFile($key)); - } - - - - /** - * Removes items from the cache by conditions & garbage collector. - * @param array conditions - * @return void - */ - public function clean(array $conds) - { - $all = !empty($conds[Cache::ALL]); - $collector = empty($conds); - - // cleaning using file iterator - if ($all || $collector) { - $now = time(); - foreach (Nette\Utils\Finder::find('*')->from($this->dir)->childFirst() as $entry) { - $path = (string) $entry; - if ($entry->isDir()) { // collector: remove empty dirs - @rmdir($path); // @ - removing dirs is not necessary - continue; - } - if ($all) { - $this->delete($path); - - } else { // collector - $meta = $this->readMetaAndLock($path, LOCK_SH); - if (!$meta) { - continue; - } - - if (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < $now) { - $this->delete($path, $meta[self::HANDLE]); - continue; - } - - flock($meta[self::HANDLE], LOCK_UN); - fclose($meta[self::HANDLE]); - } - } - - if ($this->journal) { - $this->journal->clean($conds); - } - return; - } - - // cleaning using journal - if ($this->journal) { - foreach ($this->journal->clean($conds) as $file) { - $this->delete($file); - } - } - } - - - - /** - * Reads cache data from disk. - * @param string file path - * @param int lock mode - * @return array|NULL - */ - protected function readMetaAndLock($file, $lock) - { - $handle = @fopen($file, 'r+b'); // @ - file may not exist - if (!$handle) { - return NULL; - } - - flock($handle, $lock); - - $head = stream_get_contents($handle, self::META_HEADER_LEN); - if ($head && strlen($head) === self::META_HEADER_LEN) { - $size = (int) substr($head, -6); - $meta = stream_get_contents($handle, $size, self::META_HEADER_LEN); - $meta = @unserialize($meta); // intentionally @ - if (is_array($meta)) { - fseek($handle, $size + self::META_HEADER_LEN); // needed by PHP < 5.2.6 - $meta[self::FILE] = $file; - $meta[self::HANDLE] = $handle; - return $meta; - } - } - - flock($handle, LOCK_UN); - fclose($handle); - return NULL; - } - - - - /** - * Reads cache data from disk and closes cache file handle. - * @param array - * @return mixed - */ - protected function readData($meta) - { - $data = stream_get_contents($meta[self::HANDLE]); - flock($meta[self::HANDLE], LOCK_UN); - fclose($meta[self::HANDLE]); - - if (empty($meta[self::META_SERIALIZED])) { - return $data; - } else { - return @unserialize($data); // intentionally @ - } - } - - - - /** - * Returns file name. - * @param string - * @return string - */ - protected function getCacheFile($key) - { - $file = urlencode($key); - if ($this->useDirs && $a = strrpos($file, '%00')) { // %00 = urlencode(Nette\Caching\Cache::NAMESPACE_SEPARATOR) - $file = substr_replace($file, '/_', $a, 3); - } - return $this->dir . '/_' . $file; - } - - - - /** - * Deletes and closes file. - * @param string - * @param resource - * @return void - */ - private static function delete($file, $handle = NULL) - { - if (@unlink($file)) { // @ - file may not already exist - if ($handle) { - flock($handle, LOCK_UN); - fclose($handle); - } - return; - } - - if (!$handle) { - $handle = @fopen($file, 'r+'); // @ - file may not exist - } - if ($handle) { - flock($handle, LOCK_EX); - ftruncate($handle, 0); - flock($handle, LOCK_UN); - fclose($handle); - @unlink($file); // @ - file may not already exist - } - } - -} + timestamp) + META_CALLBACKS = 'callbacks'; // array of callbacks (function, args) + + /** additional cache structure */ + const FILE = 'file', + HANDLE = 'handle'; + + + /** @var float probability that the clean() routine is started */ + public static $gcProbability = 0.001; + + /** @var bool */ + public static $useDirectories = TRUE; + + /** @var string */ + private $dir; + + /** @var bool */ + private $useDirs; + + /** @var IJournal */ + private $journal; + + /** @var array */ + private $locks; + + + + public function __construct($dir, IJournal $journal = NULL) + { + $this->dir = realpath($dir); + if ($this->dir === FALSE) { + throw new Nette\DirectoryNotFoundException("Directory '$dir' not found."); + } + + $this->useDirs = (bool) static::$useDirectories; + $this->journal = $journal; + + if (mt_rand() / mt_getrandmax() < static::$gcProbability) { + $this->clean(array()); + } + } + + + + /** + * Read from cache. + * @param string key + * @return mixed|NULL + */ + public function read($key) + { + $meta = $this->readMetaAndLock($this->getCacheFile($key), LOCK_SH); + if ($meta && $this->verify($meta)) { + return $this->readData($meta); // calls fclose() + + } else { + return NULL; + } + } + + + + /** + * Verifies dependencies. + * @param array + * @return bool + */ + private function verify($meta) + { + do { + if (!empty($meta[self::META_DELTA])) { + // meta[file] was added by readMetaAndLock() + if (filemtime($meta[self::FILE]) + $meta[self::META_DELTA] < time()) { + break; + } + touch($meta[self::FILE]); + + } elseif (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < time()) { + break; + } + + if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { + break; + } + + if (!empty($meta[self::META_ITEMS])) { + foreach ($meta[self::META_ITEMS] as $depFile => $time) { + $m = $this->readMetaAndLock($depFile, LOCK_SH); + if ($m[self::META_TIME] !== $time || ($m && !$this->verify($m))) { + break 2; + } + } + } + + return TRUE; + } while (FALSE); + + $this->delete($meta[self::FILE], $meta[self::HANDLE]); // meta[handle] & meta[file] was added by readMetaAndLock() + return FALSE; + } + + + + /** + * Prevents item reading and writing. Lock is released by write() or remove(). + * @param string key + * @return void + */ + public function lock($key) + { + $cacheFile = $this->getCacheFile($key); + if ($this->useDirs && !is_dir($dir = dirname($cacheFile))) { + @mkdir($dir, 0777); // @ - directory may already exist + } + $handle = @fopen($cacheFile, 'r+b'); // @ - file may not exist + if (!$handle) { + $handle = fopen($cacheFile, 'wb'); + if (!$handle) { + return; + } + } + + $this->locks[$key] = $handle; + flock($handle, LOCK_EX); + } + + + + /** + * Writes item into the cache. + * @param string key + * @param mixed data + * @param array dependencies + * @return void + */ + public function write($key, $data, array $dp) + { + $meta = array( + self::META_TIME => microtime(), + ); + + if (isset($dp[Cache::EXPIRATION])) { + if (empty($dp[Cache::SLIDING])) { + $meta[self::META_EXPIRE] = $dp[Cache::EXPIRATION] + time(); // absolute time + } else { + $meta[self::META_DELTA] = (int) $dp[Cache::EXPIRATION]; // sliding time + } + } + + if (isset($dp[Cache::ITEMS])) { + foreach ((array) $dp[Cache::ITEMS] as $item) { + $depFile = $this->getCacheFile($item); + $m = $this->readMetaAndLock($depFile, LOCK_SH); + $meta[self::META_ITEMS][$depFile] = $m[self::META_TIME]; // may be NULL + unset($m); + } + } + + if (isset($dp[Cache::CALLBACKS])) { + $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; + } + + if (!isset($this->locks[$key])) { + $this->lock($key); + if (!isset($this->locks[$key])) { + return; + } + } + $handle = $this->locks[$key]; + unset($this->locks[$key]); + + $cacheFile = $this->getCacheFile($key); + + if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { + if (!$this->journal) { + throw new Nette\InvalidStateException('CacheJournal has not been provided.'); + } + $this->journal->write($cacheFile, $dp); + } + + ftruncate($handle, 0); + + if (!is_string($data)) { + $data = serialize($data); + $meta[self::META_SERIALIZED] = TRUE; + } + + $head = serialize($meta) . '?>'; + $head = 'delete($cacheFile, $handle); + } + + + + /** + * Removes item from the cache. + * @param string key + * @return void + */ + public function remove($key) + { + unset($this->locks[$key]); + $this->delete($this->getCacheFile($key)); + } + + + + /** + * Removes items from the cache by conditions & garbage collector. + * @param array conditions + * @return void + */ + public function clean(array $conds) + { + $all = !empty($conds[Cache::ALL]); + $collector = empty($conds); + + // cleaning using file iterator + if ($all || $collector) { + $now = time(); + foreach (Nette\Utils\Finder::find('_*')->from($this->dir)->childFirst() as $entry) { + $path = (string) $entry; + if ($entry->isDir()) { // collector: remove empty dirs + @rmdir($path); // @ - removing dirs is not necessary + continue; + } + if ($all) { + $this->delete($path); + + } else { // collector + $meta = $this->readMetaAndLock($path, LOCK_SH); + if (!$meta) { + continue; + } + + if ((!empty($meta[self::META_DELTA]) && filemtime($meta[self::FILE]) + $meta[self::META_DELTA] < $now) + || (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < $now) + ) { + $this->delete($path, $meta[self::HANDLE]); + continue; + } + + flock($meta[self::HANDLE], LOCK_UN); + fclose($meta[self::HANDLE]); + } + } + + if ($this->journal) { + $this->journal->clean($conds); + } + return; + } + + // cleaning using journal + if ($this->journal) { + foreach ($this->journal->clean($conds) as $file) { + $this->delete($file); + } + } + } + + + + /** + * Reads cache data from disk. + * @param string file path + * @param int lock mode + * @return array|NULL + */ + protected function readMetaAndLock($file, $lock) + { + $handle = @fopen($file, 'r+b'); // @ - file may not exist + if (!$handle) { + return NULL; + } + + flock($handle, $lock); + + $head = stream_get_contents($handle, self::META_HEADER_LEN); + if ($head && strlen($head) === self::META_HEADER_LEN) { + $size = (int) substr($head, -6); + $meta = stream_get_contents($handle, $size, self::META_HEADER_LEN); + $meta = @unserialize($meta); // intentionally @ + if (is_array($meta)) { + fseek($handle, $size + self::META_HEADER_LEN); // needed by PHP < 5.2.6 + $meta[self::FILE] = $file; + $meta[self::HANDLE] = $handle; + return $meta; + } + } + + flock($handle, LOCK_UN); + fclose($handle); + return NULL; + } + + + + /** + * Reads cache data from disk and closes cache file handle. + * @param array + * @return mixed + */ + protected function readData($meta) + { + $data = stream_get_contents($meta[self::HANDLE]); + flock($meta[self::HANDLE], LOCK_UN); + fclose($meta[self::HANDLE]); + + if (empty($meta[self::META_SERIALIZED])) { + return $data; + } else { + return @unserialize($data); // intentionally @ + } + } + + + + /** + * Returns file name. + * @param string + * @return string + */ + protected function getCacheFile($key) + { + $file = urlencode($key); + if ($this->useDirs && $a = strrpos($file, '%00')) { // %00 = urlencode(Nette\Caching\Cache::NAMESPACE_SEPARATOR) + $file = substr_replace($file, '/_', $a, 3); + } + return $this->dir . '/_' . $file; + } + + + + /** + * Deletes and closes file. + * @param string + * @param resource + * @return void + */ + private static function delete($file, $handle = NULL) + { + if (@unlink($file)) { // @ - file may not already exist + if ($handle) { + flock($handle, LOCK_UN); + fclose($handle); + } + return; + } + + if (!$handle) { + $handle = @fopen($file, 'r+'); // @ - file may not exist + } + if ($handle) { + flock($handle, LOCK_EX); + ftruncate($handle, 0); + flock($handle, LOCK_UN); + fclose($handle); + @unlink($file); // @ - file may not already exist + } + } + +} diff --git a/libs/Nette/Caching/Storages/IJournal.php b/libs/Nette/Caching/Storages/IJournal.php index f041425..e5d5e9d 100644 --- a/libs/Nette/Caching/Storages/IJournal.php +++ b/libs/Nette/Caching/Storages/IJournal.php @@ -1,42 +1,42 @@ -prefix = $prefix; - $this->journal = $journal; - $this->memcache = new \Memcache; - Nette\Diagnostics\Debugger::tryError(); - $this->memcache->connect($host, $port); - if (Nette\Diagnostics\Debugger::catchError($e)) { - throw new Nette\InvalidStateException('Memcache::connect(): ' . $e->getMessage(), 0, $e); - } - } - - - - /** - * Read from cache. - * @param string key - * @return mixed|NULL - */ - public function read($key) - { - $key = $this->prefix . $key; - $meta = $this->memcache->get($key); - if (!$meta) { - return NULL; - } - - // meta structure: - // array( - // data => stored data - // delta => relative (sliding) expiration - // callbacks => array of callbacks (function, args) - // ) - - // verify dependencies - if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { - $this->memcache->delete($key, 0); - return NULL; - } - - if (!empty($meta[self::META_DELTA])) { - $this->memcache->replace($key, $meta, 0, $meta[self::META_DELTA] + time()); - } - - return $meta[self::META_DATA]; - } - - - - /** - * Writes item into the cache. - * @param string key - * @param mixed data - * @param array dependencies - * @return void - */ - public function write($key, $data, array $dp) - { - if (isset($dp[Cache::ITEMS])) { - throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.'); - } - - $key = $this->prefix . $key; - $meta = array( - self::META_DATA => $data, - ); - - $expire = 0; - if (isset($dp[Cache::EXPIRATION])) { - $expire = (int) $dp[Cache::EXPIRATION]; - if (!empty($dp[Cache::SLIDING])) { - $meta[self::META_DELTA] = $expire; // sliding time - } - } - - if (isset($dp[Cache::CALLBACKS])) { - $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; - } - - if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { - if (!$this->journal) { - throw new Nette\InvalidStateException('CacheJournal has not been provided.'); - } - $this->journal->write($key, $dp); - } - - $this->memcache->set($key, $meta, 0, $expire); - } - - - - /** - * Removes item from the cache. - * @param string key - * @return void - */ - public function remove($key) - { - $this->memcache->delete($this->prefix . $key, 0); - } - - - - /** - * Removes items from the cache by conditions & garbage collector. - * @param array conditions - * @return void - */ - public function clean(array $conds) - { - if (!empty($conds[Cache::ALL])) { - $this->memcache->flush(); - - } elseif ($this->journal) { - foreach ($this->journal->clean($conds) as $entry) { - $this->memcache->delete($entry, 0); - } - } - } - -} +prefix = $prefix; + $this->journal = $journal; + $this->memcache = new \Memcache; + if ($host) { + $this->addServer($host, $port); + } + } + + + + public function addServer($host = 'localhost', $port = 11211, $timeout = 1) + { + Nette\Diagnostics\Debugger::tryError(); + $this->memcache->addServer($host, $port, TRUE, 1, $timeout); + if (Nette\Diagnostics\Debugger::catchError($e)) { + throw new Nette\InvalidStateException('Memcache::addServer(): ' . $e->getMessage(), 0, $e); + } + } + + + + /** + * @return \Memcache + */ + public function getConnection() + { + return $this->memcache; + } + + + + /** + * Read from cache. + * @param string key + * @return mixed|NULL + */ + public function read($key) + { + $key = $this->prefix . $key; + $meta = $this->memcache->get($key); + if (!$meta) { + return NULL; + } + + // meta structure: + // array( + // data => stored data + // delta => relative (sliding) expiration + // callbacks => array of callbacks (function, args) + // ) + + // verify dependencies + if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { + $this->memcache->delete($key, 0); + return NULL; + } + + if (!empty($meta[self::META_DELTA])) { + $this->memcache->replace($key, $meta, 0, $meta[self::META_DELTA] + time()); + } + + return $meta[self::META_DATA]; + } + + + + /** + * Prevents item reading and writing. Lock is released by write() or remove(). + * @param string key + * @return void + */ + public function lock($key) + { + } + + + + /** + * Writes item into the cache. + * @param string key + * @param mixed data + * @param array dependencies + * @return void + */ + public function write($key, $data, array $dp) + { + if (isset($dp[Cache::ITEMS])) { + throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.'); + } + + $key = $this->prefix . $key; + $meta = array( + self::META_DATA => $data, + ); + + $expire = 0; + if (isset($dp[Cache::EXPIRATION])) { + $expire = (int) $dp[Cache::EXPIRATION]; + if (!empty($dp[Cache::SLIDING])) { + $meta[self::META_DELTA] = $expire; // sliding time + } + } + + if (isset($dp[Cache::CALLBACKS])) { + $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; + } + + if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { + if (!$this->journal) { + throw new Nette\InvalidStateException('CacheJournal has not been provided.'); + } + $this->journal->write($key, $dp); + } + + $this->memcache->set($key, $meta, 0, $expire); + } + + + + /** + * Removes item from the cache. + * @param string key + * @return void + */ + public function remove($key) + { + $this->memcache->delete($this->prefix . $key, 0); + } + + + + /** + * Removes items from the cache by conditions & garbage collector. + * @param array conditions + * @return void + */ + public function clean(array $conds) + { + if (!empty($conds[Cache::ALL])) { + $this->memcache->flush(); + + } elseif ($this->journal) { + foreach ($this->journal->clean($conds) as $entry) { + $this->memcache->delete($entry, 0); + } + } + } + +} diff --git a/libs/Nette/Caching/Storages/MemoryStorage.php b/libs/Nette/Caching/Storages/MemoryStorage.php index f179d47..34099e9 100644 --- a/libs/Nette/Caching/Storages/MemoryStorage.php +++ b/libs/Nette/Caching/Storages/MemoryStorage.php @@ -1,80 +1,91 @@ -data[$key]) ? $this->data[$key] : NULL; - } - - - - /** - * Writes item into the cache. - * @param string key - * @param mixed data - * @param array dependencies - * @return void - */ - public function write($key, $data, array $dp) - { - $this->data[$key] = $data; - } - - - - /** - * Removes item from the cache. - * @param string key - * @return void - */ - public function remove($key) - { - unset($this->data[$key]); - } - - - - /** - * Removes items from the cache by conditions & garbage collector. - * @param array conditions - * @return void - */ - public function clean(array $conds) - { - if (!empty($conds[Nette\Caching\Cache::ALL])) { - $this->data = array(); - } - } - -} +data[$key]) ? $this->data[$key] : NULL; + } + + + + /** + * Prevents item reading and writing. Lock is released by write() or remove(). + * @param string key + * @return void + */ + public function lock($key) + { + } + + + + /** + * Writes item into the cache. + * @param string key + * @param mixed data + * @param array dependencies + * @return void + */ + public function write($key, $data, array $dp) + { + $this->data[$key] = $data; + } + + + + /** + * Removes item from the cache. + * @param string key + * @return void + */ + public function remove($key) + { + unset($this->data[$key]); + } + + + + /** + * Removes items from the cache by conditions & garbage collector. + * @param array conditions + * @return void + */ + public function clean(array $conds) + { + if (!empty($conds[Nette\Caching\Cache::ALL])) { + $this->data = array(); + } + } + +} diff --git a/libs/Nette/Caching/Storages/PhpFileStorage.php b/libs/Nette/Caching/Storages/PhpFileStorage.php index 48faae5..2a5e451 100644 --- a/libs/Nette/Caching/Storages/PhpFileStorage.php +++ b/libs/Nette/Caching/Storages/PhpFileStorage.php @@ -1,59 +1,59 @@ - $meta[self::FILE], - 'handle' => $meta[self::HANDLE], - ); - } - - - - /** - * Returns file name. - * @param string - * @return string - */ - protected function getCacheFile($key) - { - return parent::getCacheFile(substr_replace( - $key, - trim(strtr($this->hint, '\\/@', '.._'), '.') . '-', - strpos($key, Nette\Caching\Cache::NAMESPACE_SEPARATOR) + 1, - 0 - )) . '.php'; - } - -} + $meta[self::FILE], + 'handle' => $meta[self::HANDLE], + ); + } + + + + /** + * Returns file name. + * @param string + * @return string + */ + protected function getCacheFile($key) + { + return parent::getCacheFile(substr_replace( + $key, + trim(strtr($this->hint, '\\/@', '.._'), '.') . '-', + strpos($key, Nette\Caching\Cache::NAMESPACE_SEPARATOR) + 1, + 0 + )) . '.php'; + } + +} diff --git a/libs/Nette/ComponentModel/Component.php b/libs/Nette/ComponentModel/Component.php index 705a645..6a4e4ec 100644 --- a/libs/Nette/ComponentModel/Component.php +++ b/libs/Nette/ComponentModel/Component.php @@ -1,342 +1,351 @@ - [obj, depth, path, is_monitored?]] */ - private $monitors = array(); - - - - /** - */ - public function __construct(IContainer $parent = NULL, $name = NULL) - { - if ($parent !== NULL) { - $parent->addComponent($this, $name); - - } elseif (is_string($name)) { - $this->name = $name; - } - } - - - - /** - * Lookup hierarchy for component specified by class or interface name. - * @param string class/interface type - * @param bool throw exception if component doesn't exist? - * @return IComponent - */ - public function lookup($type, $need = TRUE) - { - if (!isset($this->monitors[$type])) { // not monitored or not processed yet - $obj = $this->parent; - $path = self::NAME_SEPARATOR . $this->name; - $depth = 1; - while ($obj !== NULL) { - if ($obj instanceof $type) { - break; - } - $path = self::NAME_SEPARATOR . $obj->getName() . $path; - $depth++; - $obj = $obj->getParent(); // IComponent::getParent() - if ($obj === $this) { - $obj = NULL; // prevent cycling - } - } - - if ($obj) { - $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE); - - } else { - $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); // not found - } - } - - if ($need && $this->monitors[$type][0] === NULL) { - throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'."); - } - - return $this->monitors[$type][0]; - } - - - - /** - * Lookup for component specified by class or interface name. Returns backtrace path. - * A path is the concatenation of component names separated by self::NAME_SEPARATOR. - * @param string class/interface type - * @param bool throw exception if component doesn't exist? - * @return string - */ - public function lookupPath($type, $need = TRUE) - { - $this->lookup($type, $need); - return $this->monitors[$type][2]; - } - - - - /** - * Starts monitoring. - * @param string class/interface type - * @return void - */ - public function monitor($type) - { - if (empty($this->monitors[$type][3])) { - if ($obj = $this->lookup($type, FALSE)) { - $this->attached($obj); - } - $this->monitors[$type][3] = TRUE; // mark as monitored - } - } - - - - /** - * Stops monitoring. - * @param string class/interface type - * @return void - */ - public function unmonitor($type) - { - unset($this->monitors[$type]); - } - - - - /** - * This method will be called when the component (or component's parent) - * becomes attached to a monitored object. Do not call this method yourself. - * @param IComponent - * @return void - */ - protected function attached($obj) - { - } - - - - /** - * This method will be called before the component (or component's parent) - * becomes detached from a monitored object. Do not call this method yourself. - * @param IComponent - * @return void - */ - protected function detached($obj) - { - } - - - - /********************* interface IComponent ****************d*g**/ - - - - /** - * @return string - */ - final public function getName() - { - return $this->name; - } - - - - /** - * Returns the container if any. - * @return IContainer|NULL - */ - final public function getParent() - { - return $this->parent; - } - - - - /** - * Sets the parent of this component. This method is managed by containers and should - * not be called by applications - * @param IContainer New parent or null if this component is being removed from a parent - * @param string - * @return Component provides a fluent interface - * @throws Nette\InvalidStateException - * @internal - */ - public function setParent(IContainer $parent = NULL, $name = NULL) - { - if ($parent === NULL && $this->parent === NULL && $name !== NULL) { - $this->name = $name; // just rename - return $this; - - } elseif ($parent === $this->parent && $name === NULL) { - return $this; // nothing to do - } - - // A component cannot be given a parent if it already has a parent. - if ($this->parent !== NULL && $parent !== NULL) { - throw new Nette\InvalidStateException("Component '$this->name' already has a parent."); - } - - // remove from parent? - if ($parent === NULL) { - $this->refreshMonitors(0); - $this->parent = NULL; - - } else { // add to parent - $this->validateParent($parent); - $this->parent = $parent; - if ($name !== NULL) { - $this->name = $name; - } - - $tmp = array(); - $this->refreshMonitors(0, $tmp); - } - return $this; - } - - - - /** - * Is called by a component when it is about to be set new parent. Descendant can - * override this method to disallow a parent change by throwing an Nette\InvalidStateException - * @param IContainer - * @return void - * @throws Nette\InvalidStateException - */ - protected function validateParent(IContainer $parent) - { - } - - - - /** - * Refreshes monitors. - * @param int - * @param array|NULL (array = attaching, NULL = detaching) - * @param array - * @return void - */ - private function refreshMonitors($depth, & $missing = NULL, & $listeners = array()) - { - if ($this instanceof IContainer) { - foreach ($this->getComponents() as $component) { - if ($component instanceof Component) { - $component->refreshMonitors($depth + 1, $missing, $listeners); - } - } - } - - if ($missing === NULL) { // detaching - foreach ($this->monitors as $type => $rec) { - if (isset($rec[1]) && $rec[1] > $depth) { - if ($rec[3]) { // monitored - $this->monitors[$type] = array(NULL, NULL, NULL, TRUE); - $listeners[] = array($this, $rec[0]); - } else { // not monitored, just randomly cached - unset($this->monitors[$type]); - } - } - } - - } else { // attaching - foreach ($this->monitors as $type => $rec) { - if (isset($rec[0])) { // is in cache yet - continue; - - } elseif (!$rec[3]) { // not monitored, just randomly cached - unset($this->monitors[$type]); - - } elseif (isset($missing[$type])) { // known from previous lookup - $this->monitors[$type] = array(NULL, NULL, NULL, TRUE); - - } else { - $this->monitors[$type] = NULL; // forces re-lookup - if ($obj = $this->lookup($type, FALSE)) { - $listeners[] = array($this, $obj); - } else { - $missing[$type] = TRUE; - } - $this->monitors[$type][3] = TRUE; // mark as monitored - } - } - } - - if ($depth === 0) { // call listeners - $method = $missing === NULL ? 'detached' : 'attached'; - foreach ($listeners as $item) { - $item[0]->$method($item[1]); - } - } - } - - - - /********************* cloneable, serializable ****************d*g**/ - - - - /** - * Object cloning. - */ - public function __clone() - { - if ($this->parent === NULL) { - return; - - } elseif ($this->parent instanceof Container) { - $this->parent = $this->parent->_isCloning(); - if ($this->parent === NULL) { // not cloning - $this->refreshMonitors(0); - } - - } else { - $this->parent = NULL; - $this->refreshMonitors(0); - } - } - - - - /** - * Prevents unserialization. - */ - final public function __wakeup() - { - throw new Nette\NotImplementedException; - } - -} + [obj, depth, path, is_monitored?]] */ + private $monitors = array(); + + + + /** + */ + public function __construct(IContainer $parent = NULL, $name = NULL) + { + if ($parent !== NULL) { + $parent->addComponent($this, $name); + + } elseif (is_string($name)) { + $this->name = $name; + } + } + + + + /** + * Lookup hierarchy for component specified by class or interface name. + * @param string class/interface type + * @param bool throw exception if component doesn't exist? + * @return IComponent + */ + public function lookup($type, $need = TRUE) + { + if (!isset($this->monitors[$type])) { // not monitored or not processed yet + $obj = $this->parent; + $path = self::NAME_SEPARATOR . $this->name; + $depth = 1; + while ($obj !== NULL) { + if ($obj instanceof $type) { + break; + } + $path = self::NAME_SEPARATOR . $obj->getName() . $path; + $depth++; + $obj = $obj->getParent(); // IComponent::getParent() + if ($obj === $this) { + $obj = NULL; // prevent cycling + } + } + + if ($obj) { + $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE); + + } else { + $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); // not found + } + } + + if ($need && $this->monitors[$type][0] === NULL) { + throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'."); + } + + return $this->monitors[$type][0]; + } + + + + /** + * Lookup for component specified by class or interface name. Returns backtrace path. + * A path is the concatenation of component names separated by self::NAME_SEPARATOR. + * @param string class/interface type + * @param bool throw exception if component doesn't exist? + * @return string + */ + public function lookupPath($type, $need = TRUE) + { + $this->lookup($type, $need); + return $this->monitors[$type][2]; + } + + + + /** + * Starts monitoring. + * @param string class/interface type + * @return void + */ + public function monitor($type) + { + if (empty($this->monitors[$type][3])) { + if ($obj = $this->lookup($type, FALSE)) { + $this->attached($obj); + } + $this->monitors[$type][3] = TRUE; // mark as monitored + } + } + + + + /** + * Stops monitoring. + * @param string class/interface type + * @return void + */ + public function unmonitor($type) + { + unset($this->monitors[$type]); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param IComponent + * @return void + */ + protected function attached($obj) + { + } + + + + /** + * This method will be called before the component (or component's parent) + * becomes detached from a monitored object. Do not call this method yourself. + * @param IComponent + * @return void + */ + protected function detached($obj) + { + } + + + + /********************* interface IComponent ****************d*g**/ + + + + /** + * @return string + */ + final public function getName() + { + return $this->name; + } + + + + /** + * Returns the container if any. + * @return IContainer|NULL + */ + final public function getParent() + { + return $this->parent; + } + + + + /** + * Sets the parent of this component. This method is managed by containers and should + * not be called by applications + * @param IContainer New parent or null if this component is being removed from a parent + * @param string + * @return Component provides a fluent interface + * @throws Nette\InvalidStateException + * @internal + */ + public function setParent(IContainer $parent = NULL, $name = NULL) + { + if ($parent === NULL && $this->parent === NULL && $name !== NULL) { + $this->name = $name; // just rename + return $this; + + } elseif ($parent === $this->parent && $name === NULL) { + return $this; // nothing to do + } + + // A component cannot be given a parent if it already has a parent. + if ($this->parent !== NULL && $parent !== NULL) { + throw new Nette\InvalidStateException("Component '$this->name' already has a parent."); + } + + // remove from parent? + if ($parent === NULL) { + $this->refreshMonitors(0); + $this->parent = NULL; + + } else { // add to parent + $this->validateParent($parent); + $this->parent = $parent; + if ($name !== NULL) { + $this->name = $name; + } + + $tmp = array(); + $this->refreshMonitors(0, $tmp); + } + return $this; + } + + + + /** + * Is called by a component when it is about to be set new parent. Descendant can + * override this method to disallow a parent change by throwing an Nette\InvalidStateException + * @return void + * @throws Nette\InvalidStateException + */ + protected function validateParent(IContainer $parent) + { + } + + + + /** + * Refreshes monitors. + * @param int + * @param array|NULL (array = attaching, NULL = detaching) + * @param array + * @return void + */ + private function refreshMonitors($depth, & $missing = NULL, & $listeners = array()) + { + if ($this instanceof IContainer) { + foreach ($this->getComponents() as $component) { + if ($component instanceof Component) { + $component->refreshMonitors($depth + 1, $missing, $listeners); + } + } + } + + if ($missing === NULL) { // detaching + foreach ($this->monitors as $type => $rec) { + if (isset($rec[1]) && $rec[1] > $depth) { + if ($rec[3]) { // monitored + $this->monitors[$type] = array(NULL, NULL, NULL, TRUE); + $listeners[] = array($this, $rec[0]); + } else { // not monitored, just randomly cached + unset($this->monitors[$type]); + } + } + } + + } else { // attaching + foreach ($this->monitors as $type => $rec) { + if (isset($rec[0])) { // is in cache yet + continue; + + } elseif (!$rec[3]) { // not monitored, just randomly cached + unset($this->monitors[$type]); + + } elseif (isset($missing[$type])) { // known from previous lookup + $this->monitors[$type] = array(NULL, NULL, NULL, TRUE); + + } else { + $this->monitors[$type] = NULL; // forces re-lookup + if ($obj = $this->lookup($type, FALSE)) { + $listeners[] = array($this, $obj); + } else { + $missing[$type] = TRUE; + } + $this->monitors[$type][3] = TRUE; // mark as monitored + } + } + } + + if ($depth === 0) { // call listeners + $method = $missing === NULL ? 'detached' : 'attached'; + foreach ($listeners as $item) { + $item[0]->$method($item[1]); + } + } + } + + + + /********************* cloneable, serializable ****************d*g**/ + + + + /** + * Object cloning. + */ + public function __clone() + { + if ($this->parent === NULL) { + return; + + } elseif ($this->parent instanceof Container) { + $this->parent = $this->parent->_isCloning(); + if ($this->parent === NULL) { // not cloning + $this->refreshMonitors(0); + } + + } else { + $this->parent = NULL; + $this->refreshMonitors(0); + } + } + + + + /** + * Prevents serialization. + */ + final public function __sleep() + { + throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this)); + } + + + + /** + * Prevents unserialization. + */ + final public function __wakeup() + { + throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this)); + } + +} diff --git a/libs/Nette/ComponentModel/Container.php b/libs/Nette/ComponentModel/Container.php index fb5693a..9c35767 100644 --- a/libs/Nette/ComponentModel/Container.php +++ b/libs/Nette/ComponentModel/Container.php @@ -1,257 +1,256 @@ -getName(); - } - - if (is_int($name)) { - $name = (string) $name; - - } elseif (!is_string($name)) { - throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given."); - - } elseif (!preg_match('#^[a-zA-Z0-9_]+$#', $name)) { - throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); - } - - if (isset($this->components[$name])) { - throw new Nette\InvalidStateException("Component with name '$name' already exists."); - } - - // check circular reference - $obj = $this; - do { - if ($obj === $component) { - throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'."); - } - $obj = $obj->getParent(); - } while ($obj !== NULL); - - // user checking - $this->validateChildComponent($component); - - try { - if (isset($this->components[$insertBefore])) { - $tmp = array(); - foreach ($this->components as $k => $v) { - if ($k === $insertBefore) { - $tmp[$name] = $component; - } - $tmp[$k] = $v; - } - $this->components = $tmp; - } else { - $this->components[$name] = $component; - } - $component->setParent($this, $name); - - } catch (\Exception $e) { - unset($this->components[$name]); // undo - throw $e; - } - } - - - - /** - * Removes a component from the IComponentContainer. - * @param IComponent - * @return void - */ - public function removeComponent(IComponent $component) - { - $name = $component->getName(); - if (!isset($this->components[$name]) || $this->components[$name] !== $component) { - throw new Nette\InvalidArgumentException("Component named '$name' is not located in this container."); - } - - unset($this->components[$name]); - $component->setParent(NULL); - } - - - - /** - * Returns component specified by name or path. - * @param string - * @param bool throw exception if component doesn't exist? - * @return IComponent|NULL - */ - final public function getComponent($name, $need = TRUE) - { - if (is_int($name)) { - $name = (string) $name; - - } elseif (!is_string($name)) { - throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given."); - - } else { - $a = strpos($name, self::NAME_SEPARATOR); - if ($a !== FALSE) { - $ext = (string) substr($name, $a + 1); - $name = substr($name, 0, $a); - } - - if ($name === '') { - throw new Nette\InvalidArgumentException("Component or subcomponent name must not be empty string."); - } - } - - if (!isset($this->components[$name])) { - $component = $this->createComponent($name); - if ($component instanceof IComponent && $component->getParent() === NULL) { - $this->addComponent($component, $name); - } - } - - if (isset($this->components[$name])) { - if (!isset($ext)) { - return $this->components[$name]; - - } elseif ($this->components[$name] instanceof IContainer) { - return $this->components[$name]->getComponent($ext, $need); - - } elseif ($need) { - throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component."); - } - - } elseif ($need) { - throw new Nette\InvalidArgumentException("Component with name '$name' does not exist."); - } - } - - - - /** - * Component factory. Delegates the creation of components to a createComponent method. - * @param string component name - * @return IComponent the created component (optionally) - */ - protected function createComponent($name) - { - $ucname = ucfirst($name); - $method = 'createComponent' . $ucname; - if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) { - $component = $this->$method($name); - if (!$component instanceof IComponent && !isset($this->components[$name])) { - $class = get_class($this); - throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component."); - } - return $component; - } - } - - - - /** - * Iterates over a components. - * @param bool recursive? - * @param string class types filter - * @return \ArrayIterator - */ - final public function getComponents($deep = FALSE, $filterType = NULL) - { - $iterator = new RecursiveComponentIterator($this->components); - if ($deep) { - $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST; - $iterator = new \RecursiveIteratorIterator($iterator, $deep); - } - if ($filterType) { - $iterator = new Nette\Iterators\InstanceFilter($iterator, $filterType); - } - return $iterator; - } - - - - /** - * Descendant can override this method to disallow insert a child by throwing an Nette\InvalidStateException. - * @param IComponent - * @return void - * @throws Nette\InvalidStateException - */ - protected function validateChildComponent(IComponent $child) - { - } - - - - /********************* cloneable, serializable ****************d*g**/ - - - - /** - * Object cloning. - */ - public function __clone() - { - if ($this->components) { - $oldMyself = reset($this->components)->getParent(); - $oldMyself->cloning = $this; - foreach ($this->components as $name => $component) { - $this->components[$name] = clone $component; - } - $oldMyself->cloning = NULL; - } - parent::__clone(); - } - - - - /** - * Is container cloning now? - * @return NULL|IComponent - * @internal - */ - public function _isCloning() - { - return $this->cloning; - } - -} +getName(); + } + + if (is_int($name)) { + $name = (string) $name; + + } elseif (!is_string($name)) { + throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given."); + + } elseif (!preg_match('#^[a-zA-Z0-9_]+$#', $name)) { + throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); + } + + if (isset($this->components[$name])) { + throw new Nette\InvalidStateException("Component with name '$name' already exists."); + } + + // check circular reference + $obj = $this; + do { + if ($obj === $component) { + throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'."); + } + $obj = $obj->getParent(); + } while ($obj !== NULL); + + // user checking + $this->validateChildComponent($component); + + try { + if (isset($this->components[$insertBefore])) { + $tmp = array(); + foreach ($this->components as $k => $v) { + if ($k === $insertBefore) { + $tmp[$name] = $component; + } + $tmp[$k] = $v; + } + $this->components = $tmp; + } else { + $this->components[$name] = $component; + } + $component->setParent($this, $name); + + } catch (\Exception $e) { + unset($this->components[$name]); // undo + throw $e; + } + return $this; + } + + + + /** + * Removes a component from the IContainer. + * @return void + */ + public function removeComponent(IComponent $component) + { + $name = $component->getName(); + if (!isset($this->components[$name]) || $this->components[$name] !== $component) { + throw new Nette\InvalidArgumentException("Component named '$name' is not located in this container."); + } + + unset($this->components[$name]); + $component->setParent(NULL); + } + + + + /** + * Returns component specified by name or path. + * @param string + * @param bool throw exception if component doesn't exist? + * @return IComponent|NULL + */ + final public function getComponent($name, $need = TRUE) + { + if (is_int($name)) { + $name = (string) $name; + + } elseif (!is_string($name)) { + throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given."); + + } else { + $a = strpos($name, self::NAME_SEPARATOR); + if ($a !== FALSE) { + $ext = (string) substr($name, $a + 1); + $name = substr($name, 0, $a); + } + + if ($name === '') { + throw new Nette\InvalidArgumentException("Component or subcomponent name must not be empty string."); + } + } + + if (!isset($this->components[$name])) { + $component = $this->createComponent($name); + if ($component instanceof IComponent && $component->getParent() === NULL) { + $this->addComponent($component, $name); + } + } + + if (isset($this->components[$name])) { + if (!isset($ext)) { + return $this->components[$name]; + + } elseif ($this->components[$name] instanceof IContainer) { + return $this->components[$name]->getComponent($ext, $need); + + } elseif ($need) { + throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component."); + } + + } elseif ($need) { + throw new Nette\InvalidArgumentException("Component with name '$name' does not exist."); + } + } + + + + /** + * Component factory. Delegates the creation of components to a createComponent method. + * @param string component name + * @return IComponent the created component (optionally) + */ + protected function createComponent($name) + { + $ucname = ucfirst($name); + $method = 'createComponent' . $ucname; + if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) { + $component = $this->$method($name); + if (!$component instanceof IComponent && !isset($this->components[$name])) { + $class = get_class($this); + throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component."); + } + return $component; + } + } + + + + /** + * Iterates over a components. + * @param bool recursive? + * @param string class types filter + * @return \ArrayIterator + */ + final public function getComponents($deep = FALSE, $filterType = NULL) + { + $iterator = new RecursiveComponentIterator($this->components); + if ($deep) { + $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST; + $iterator = new \RecursiveIteratorIterator($iterator, $deep); + } + if ($filterType) { + $iterator = new Nette\Iterators\InstanceFilter($iterator, $filterType); + } + return $iterator; + } + + + + /** + * Descendant can override this method to disallow insert a child by throwing an Nette\InvalidStateException. + * @return void + * @throws Nette\InvalidStateException + */ + protected function validateChildComponent(IComponent $child) + { + } + + + + /********************* cloneable, serializable ****************d*g**/ + + + + /** + * Object cloning. + */ + public function __clone() + { + if ($this->components) { + $oldMyself = reset($this->components)->getParent(); + $oldMyself->cloning = $this; + foreach ($this->components as $name => $component) { + $this->components[$name] = clone $component; + } + $oldMyself->cloning = NULL; + } + parent::__clone(); + } + + + + /** + * Is container cloning now? + * @return NULL|IComponent + * @internal + */ + public function _isCloning() + { + return $this->cloning; + } + +} diff --git a/libs/Nette/ComponentModel/IComponent.php b/libs/Nette/ComponentModel/IComponent.php index bb994bf..68f5ed4 100644 --- a/libs/Nette/ComponentModel/IComponent.php +++ b/libs/Nette/ComponentModel/IComponent.php @@ -1,47 +1,47 @@ -current() instanceof IContainer; - } - - - - /** - * The sub-iterator for the current element. - * @return \RecursiveIterator - */ - public function getChildren() - { - return $this->current()->getComponents(); - } - - - - /** - * Returns the count of elements. - * @return int - */ - public function count() - { - return iterator_count($this); - } - -} +current() instanceof IContainer; + } + + + + /** + * The sub-iterator for the current element. + * @return \RecursiveIterator + */ + public function getChildren() + { + return $this->current()->getComponents(); + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + return iterator_count($this); + } + +} diff --git a/libs/Nette/Config/Adapters/IniAdapter.php b/libs/Nette/Config/Adapters/IniAdapter.php new file mode 100644 index 0000000..d8a820b --- /dev/null +++ b/libs/Nette/Config/Adapters/IniAdapter.php @@ -0,0 +1,151 @@ +getMessage(), 0, $e); + } + + $data = array(); + foreach ($ini as $secName => $secData) { + if (is_array($secData)) { // is section? + if (substr($secName, -1) === self::RAW_SECTION) { + $secName = substr($secName, 0, -1); + } else { // process key nesting separator (key1.key2.key3) + $tmp = array(); + foreach ($secData as $key => $val) { + $cursor = & $tmp; + $key = str_replace(self::ESCAPED_KEY_SEPARATOR, "\xFF", $key); + foreach (explode(self::KEY_SEPARATOR, $key) as $part) { + $part = str_replace("\xFF", self::KEY_SEPARATOR, $part); + if (!isset($cursor[$part]) || is_array($cursor[$part])) { + $cursor = & $cursor[$part]; + } else { + throw new Nette\InvalidStateException("Invalid key '$key' in section [$secName] in file '$file'."); + } + } + $cursor = $val; + } + $secData = $tmp; + } + + $parts = explode(self::INHERITING_SEPARATOR, $secName); + if (count($parts) > 1) { + $secName = trim($parts[0]); + $secData[Helpers::EXTENDS_KEY] = trim($parts[1]); + } + } + + $cursor = & $data; // nesting separator in section name + foreach (explode(self::KEY_SEPARATOR, $secName) as $part) { + if (!isset($cursor[$part]) || is_array($cursor[$part])) { + $cursor = & $cursor[$part]; + } else { + throw new Nette\InvalidStateException("Invalid section [$secName] in file '$file'."); + } + } + + if (is_array($secData) && is_array($cursor)) { + $secData = Helpers::merge($secData, $cursor); + } + + $cursor = $secData; + } + + return $data; + } + + + + /** + * Generates configuration in INI format. + * @param array + * @return string + */ + public function dump(array $data) + { + $output = array(); + foreach ($data as $name => $secData) { + if (!is_array($secData)) { + $output = array(); + self::build($data, $output, ''); + break; + } + if ($parent = Helpers::takeParent($secData)) { + $output[] = "[$name " . self::INHERITING_SEPARATOR . " $parent]"; + } else { + $output[] = "[$name]"; + } + self::build($secData, $output, ''); + $output[] = ''; + } + return "; generated by Nette\n\n" . implode(PHP_EOL, $output); + } + + + + /** + * Recursive builds INI list. + * @return void + */ + private static function build($input, & $output, $prefix) + { + foreach ($input as $key => $val) { + $key = str_replace(self::KEY_SEPARATOR, self::ESCAPED_KEY_SEPARATOR, $key); + if (is_array($val)) { + self::build($val, $output, $prefix . $key . self::KEY_SEPARATOR); + + } elseif (is_bool($val)) { + $output[] = "$prefix$key = " . ($val ? 'true' : 'false'); + + } elseif (is_numeric($val)) { + $output[] = "$prefix$key = $val"; + + } elseif (is_string($val)) { + $output[] = "$prefix$key = \"$val\""; + + } else { + throw new Nette\InvalidArgumentException("The '$prefix$key' item must be scalar or array, " . gettype($val) ." given."); + } + } + } + +} diff --git a/libs/Nette/Config/Adapters/NeonAdapter.php b/libs/Nette/Config/Adapters/NeonAdapter.php new file mode 100644 index 0000000..df23d1b --- /dev/null +++ b/libs/Nette/Config/Adapters/NeonAdapter.php @@ -0,0 +1,93 @@ +process((array) Neon::decode(file_get_contents($file))); + } + + + + private function process(array $arr) + { + $res = array(); + foreach ($arr as $key => $val) { + if (substr($key, -1) === self::PREVENT_MERGING) { + if (!is_array($val) && $val !== NULL) { + throw new Nette\InvalidStateException("Replacing operator is available only for arrays, item '$key' is not array."); + } + $key = substr($key, 0, -1); + $val[Helpers::EXTENDS_KEY] = Helpers::OVERWRITE; + + } elseif (preg_match('#^(\S+)\s+' . self::INHERITING_SEPARATOR . '\s+(\S+)$#', $key, $matches)) { + if (!is_array($val) && $val !== NULL) { + throw new Nette\InvalidStateException("Inheritance operator is available only for arrays, item '$key' is not array."); + } + list(, $key, $val[Helpers::EXTENDS_KEY]) = $matches; + if (isset($res[$key])) { + throw new Nette\InvalidStateException("Duplicated key '$key'."); + } + } + + if (is_array($val)) { + $val = $this->process($val); + } elseif ($val instanceof Nette\Utils\NeonEntity) { + $val = (object) array('value' => $val->value, 'attributes' => $this->process($val->attributes)); + } + $res[$key] = $val; + } + return $res; + } + + + + /** + * Generates configuration in NEON format. + * @param array + * @return string + */ + public function dump(array $data) + { + $tmp = array(); + foreach ($data as $name => $secData) { + if ($parent = Helpers::takeParent($secData)) { + $name .= ' ' . self::INHERITING_SEPARATOR . ' ' . $parent; + } + $tmp[$name] = $secData; + } + return "# generated by Nette\n\n" . Neon::encode($tmp, Neon::BLOCK); + } + +} diff --git a/libs/Nette/Config/Adapters/PhpAdapter.php b/libs/Nette/Config/Adapters/PhpAdapter.php new file mode 100644 index 0000000..a6c5cc8 --- /dev/null +++ b/libs/Nette/Config/Adapters/PhpAdapter.php @@ -0,0 +1,48 @@ + 1, 'factories' => 1, 'parameters' => 1); + + + + /** + * Add custom configurator extension. + * @return Compiler provides a fluent interface + */ + public function addExtension($name, CompilerExtension $extension) + { + if (isset(self::$reserved[$name])) { + throw new Nette\InvalidArgumentException("Name '$name' is reserved."); + } + $this->extensions[$name] = $extension->setCompiler($this, $name); + return $this; + } + + + + /** + * @return array + */ + public function getExtensions() + { + return $this->extensions; + } + + + + /** + * @return Nette\DI\ContainerBuilder + */ + public function getContainerBuilder() + { + return $this->container; + } + + + + /** + * Returns configuration without expanded parameters. + * @return array + */ + public function getConfig() + { + return $this->config; + } + + + + /** + * @return string + */ + public function compile(array $config, $className, $parentName) + { + $this->config = $config; + $this->container = new Nette\DI\ContainerBuilder; + $this->processParameters(); + $this->processExtensions(); + $this->processServices(); + return $this->generateCode($className, $parentName); + } + + + + public function processParameters() + { + if (isset($this->config['parameters'])) { + $this->container->parameters = $this->config['parameters']; + } + } + + + + public function processExtensions() + { + for ($i = 0; $slice = array_slice($this->extensions, $i, 1); $i++) { + reset($slice)->loadConfiguration(); + } + + if ($extra = array_diff_key($this->config, self::$reserved, $this->extensions)) { + $extra = implode("', '", array_keys($extra)); + throw new Nette\InvalidStateException("Found sections '$extra' in configuration, but corresponding extensions are missing."); + } + } + + + + public function processServices() + { + $this->parseServices($this->container, $this->config); + + foreach ($this->extensions as $name => $extension) { + $this->container->addDefinition($name) + ->setClass('Nette\DI\NestedAccessor', array('@container', $name)) + ->setAutowired(FALSE); + + if (isset($this->config[$name])) { + $this->parseServices($this->container, $this->config[$name], $name); + } + } + + foreach ($this->container->getDefinitions() as $name => $def) { + $factory = $name . 'Factory'; + if (!$def->shared && !$def->internal && !$this->container->hasDefinition($factory)) { + $this->container->addDefinition($factory) + ->setClass('Nette\Callback', array('@container', Nette\DI\Container::getMethodName($name, FALSE))) + ->setAutowired(FALSE) + ->tags = $def->tags; + } + } + } + + + + public function generateCode($className, $parentName) + { + foreach ($this->extensions as $extension) { + $extension->beforeCompile(); + $this->container->addDependency(Nette\Reflection\ClassType::from($extension)->getFileName()); + } + + $classes[] = $class = $this->container->generateClass($parentName); + $class->setName($className) + ->addMethod('initialize'); + + foreach ($this->extensions as $extension) { + $extension->afterCompile($class); + } + + $defs = $this->container->getDefinitions(); + ksort($defs); + $list = array_keys($defs); + foreach (array_reverse($defs, TRUE) as $name => $def) { + if ($def->class === 'Nette\DI\NestedAccessor' && ($found = preg_grep('#^'.$name.'\.#i', $list))) { + $list = array_diff($list, $found); + $def->class = $className . '_' . preg_replace('#\W+#', '_', $name); + $class->documents = preg_replace("#\S+(?= \\$$name$)#", $def->class, $class->documents); + $classes[] = $accessor = new Nette\Utils\PhpGenerator\ClassType($def->class); + foreach ($found as $item) { + if ($defs[$item]->internal) { + continue; + } + $short = substr($item, strlen($name) + 1); + $accessor->addDocument($defs[$item]->shared + ? "@property {$defs[$item]->class} \$$short" + : "@method {$defs[$item]->class} create" . ucfirst("$short()")); + } + } + } + + return implode("\n\n\n", $classes); + } + + + + /********************* tools ****************d*g**/ + + + + /** + * Parses section 'services' from configuration file. + * @return void + */ + public static function parseServices(Nette\DI\ContainerBuilder $container, array $config, $namespace = NULL) + { + $services = isset($config['services']) ? $config['services'] : array(); + $factories = isset($config['factories']) ? $config['factories'] : array(); + if ($tmp = array_intersect_key($services, $factories)) { + $tmp = implode("', '", array_keys($tmp)); + throw new Nette\DI\ServiceCreationException("It is not allowed to use services and factories with the same names: '$tmp'."); + } + + $all = $services + $factories; + uasort($all, function($a, $b) { + return strcmp(Helpers::isInheriting($a), Helpers::isInheriting($b)); + }); + + foreach ($all as $name => $def) { + $shared = array_key_exists($name, $services); + $name = ($namespace ? $namespace . '.' : '') . $name; + + if (($parent = Helpers::takeParent($def)) && $parent !== $name) { + $container->removeDefinition($name); + $definition = $container->addDefinition($name); + if ($parent !== Helpers::OVERWRITE) { + foreach ($container->getDefinition($parent) as $k => $v) { + $definition->$k = unserialize(serialize($v)); // deep clone + } + } + } elseif ($container->hasDefinition($name)) { + $definition = $container->getDefinition($name); + if ($definition->shared !== $shared) { + throw new Nette\DI\ServiceCreationException("It is not allowed to use service and factory with the same name '$name'."); + } + } else { + $definition = $container->addDefinition($name); + } + try { + static::parseService($definition, $def, $shared); + } catch (\Exception $e) { + throw new Nette\DI\ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); + } + } + } + + + + /** + * Parses single service from configuration file. + * @return void + */ + public static function parseService(Nette\DI\ServiceDefinition $definition, $config, $shared = TRUE) + { + if ($config === NULL) { + return; + } elseif (!is_array($config)) { + $config = array('class' => NULL, 'factory' => $config); + } + + $known = $shared + ? array('class', 'factory', 'arguments', 'setup', 'autowired', 'run', 'tags') + : array('class', 'factory', 'arguments', 'setup', 'autowired', 'tags', 'internal', 'parameters'); + + if ($error = array_diff(array_keys($config), $known)) { + throw new Nette\InvalidStateException("Unknown key '" . implode("', '", $error) . "' in definition of service."); + } + + $arguments = array(); + if (array_key_exists('arguments', $config)) { + Validators::assertField($config, 'arguments', 'array'); + $arguments = self::filterArguments($config['arguments']); + $definition->setArguments($arguments); + } + + if (array_key_exists('class', $config) || array_key_exists('factory', $config)) { + $definition->class = NULL; + $definition->factory = NULL; + } + + if (array_key_exists('class', $config)) { + Validators::assertField($config, 'class', 'string|stdClass|null'); + if ($config['class'] instanceof \stdClass) { + $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes)); + } else { + $definition->setClass($config['class'], $arguments); + } + } + + if (array_key_exists('factory', $config)) { + Validators::assertField($config, 'factory', 'callable|stdClass|null'); + if ($config['factory'] instanceof \stdClass) { + $definition->setFactory($config['factory']->value, self::filterArguments($config['factory']->attributes)); + } else { + $definition->setFactory($config['factory'], $arguments); + } + } + + if (isset($config['setup'])) { + if (Helpers::takeParent($config['setup'])) { + $definition->setup = array(); + } + Validators::assertField($config, 'setup', 'list'); + foreach ($config['setup'] as $id => $setup) { + Validators::assert($setup, 'callable|stdClass', "setup item #$id"); + if ($setup instanceof \stdClass) { + Validators::assert($setup->value, 'callable', "setup item #$id"); + $definition->addSetup($setup->value, self::filterArguments($setup->attributes)); + } else { + $definition->addSetup($setup); + } + } + } + + $definition->setShared($shared); + if (isset($config['parameters'])) { + Validators::assertField($config, 'parameters', 'array'); + $definition->setParameters($config['parameters']); + } + + if (isset($config['autowired'])) { + Validators::assertField($config, 'autowired', 'bool'); + $definition->setAutowired($config['autowired']); + } + + if (isset($config['internal'])) { + Validators::assertField($config, 'internal', 'bool'); + $definition->setInternal($config['internal']); + } + + if (isset($config['run'])) { + $config['tags']['run'] = (bool) $config['run']; + } + + if (isset($config['tags'])) { + Validators::assertField($config, 'tags', 'array'); + if (Helpers::takeParent($config['tags'])) { + $definition->tags = array(); + } + foreach ($config['tags'] as $tag => $attrs) { + if (is_int($tag) && is_string($attrs)) { + $definition->addTag($attrs); + } else { + $definition->addTag($tag, $attrs); + } + } + } + } + + + + /** + * Removes ... and replaces entities with Nette\DI\Statement. + * @return array + */ + public static function filterArguments(array $args) + { + foreach ($args as $k => $v) { + if ($v === '...') { + unset($args[$k]); + } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) { + $args[$k] = new Nette\DI\Statement($v->value, self::filterArguments($v->attributes)); + } + } + return $args; + } + +} diff --git a/libs/Nette/Config/CompilerExtension.php b/libs/Nette/Config/CompilerExtension.php new file mode 100644 index 0000000..5c66e8e --- /dev/null +++ b/libs/Nette/Config/CompilerExtension.php @@ -0,0 +1,130 @@ +compiler = $compiler; + $this->name = $name; + return $this; + } + + + + /** + * Returns extension configuration. + * @param array default values. + * @param bool perform %parameters% expansion? + * @return array + */ + public function getConfig(array $defaults = NULL, $expand = TRUE) + { + $config = $this->compiler->getConfig(); + $config = isset($config[$this->name]) ? $config[$this->name] : array(); + unset($config['services'], $config['factories']); + $config = Helpers::merge($config, $defaults); + return $expand ? $this->compiler->getContainerBuilder()->expand($config) : $config; + } + + + + /** + * @return Nette\DI\ContainerBuilder + */ + public function getContainerBuilder() + { + return $this->compiler->getContainerBuilder(); + } + + + + /** + * Reads configuration from file. + * @param string file name + * @return array + */ + public function loadFromFile($file) + { + $loader = new Loader; + $res = $loader->load($file); + $container = $this->compiler->getContainerBuilder(); + foreach ($loader->getDependencies() as $file) { + $container->addDependency($file); + } + return $res; + } + + + + /** + * Prepend extension name to identifier or service name. + * @param string + * @return string + */ + public function prefix($id) + { + return substr_replace($id, $this->name . '.', substr($id, 0, 1) === '@' ? 1 : 0, 0); + } + + + + /** + * Processes configuration data. Intended to be overridden by descendant. + * @return void + */ + public function loadConfiguration() + { + } + + + + /** + * Adjusts DI container before is compiled to PHP class. Intended to be overridden by descendant. + * @return void + */ + public function beforeCompile() + { + } + + + + /** + * Adjusts DI container compiled to PHP class. Intended to be overridden by descendant. + * @return void + */ + public function afterCompile(Nette\Utils\PhpGenerator\ClassType $class) + { + } + +} diff --git a/libs/Nette/Config/Config.php b/libs/Nette/Config/Config.php deleted file mode 100644 index 5ae22b6..0000000 --- a/libs/Nette/Config/Config.php +++ /dev/null @@ -1,104 +0,0 @@ - 'Nette\Config\IniAdapter', - 'neon' => 'Nette\Config\NeonAdapter', - ); - - - - /** - * Static class - cannot be instantiated. - */ - final public function __construct() - { - throw new Nette\StaticClassException; - } - - - - /** - * Registers adapter for given file extension. - * @param string file extension - * @param string class name (IConfigAdapter) - * @return void - */ - public static function registerExtension($extension, $class) - { - if (!class_exists($class)) { - throw new Nette\InvalidArgumentException("Class '$class' was not found."); - } - - if (!Nette\Reflection\ClassType::from($class)->implementsInterface('Nette\Config\IAdapter')) { - throw new Nette\InvalidArgumentException("Configuration adapter '$class' is not Nette\\Config\\IAdapter implementor."); - } - - self::$extensions[strtolower($extension)] = $class; - } - - - - /** - * Creates new configuration object from file. - * @param string file name - * @param string section to load - * @return array - */ - public static function fromFile($file, $section = NULL) - { - $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); - if (!isset(self::$extensions[$extension])) { - throw new Nette\InvalidArgumentException("Unknown file extension '$file'."); - } - - $data = call_user_func(array(self::$extensions[$extension], 'load'), $file, $section); - if ($section) { - if (!isset($data[$section]) || !is_array($data[$section])) { - throw new Nette\InvalidStateException("There is not section [$section] in file '$file'."); - } - $data = $data[$section]; - } - return $data; - } - - - - /** - * Save configuration to file. - * @param mixed - * @param string file - * @return void - */ - public static function save($config, $file) - { - $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); - if (!isset(self::$extensions[$extension])) { - throw new Nette\InvalidArgumentException("Unknown file extension '$file'."); - } - return call_user_func(array(self::$extensions[$extension], 'save'), $config, $file); - } - -} diff --git a/libs/Nette/Config/Configurator.php b/libs/Nette/Config/Configurator.php new file mode 100644 index 0000000..d3b2e9b --- /dev/null +++ b/libs/Nette/Config/Configurator.php @@ -0,0 +1,342 @@ +parameters = $this->getDefaultParameters(); + } + + + + /** + * Set parameter %debugMode%. + * @param bool|string|array + * @return Configurator provides a fluent interface + */ + public function setDebugMode($value = TRUE) + { + $this->parameters['debugMode'] = is_bool($value) ? $value : self::detectDebugMode($value); + $this->parameters['productionMode'] = !$this->parameters['debugMode']; // compatibility + return $this; + } + + + + /** + * @return bool + */ + public function isDebugMode() + { + return !$this->parameters['productionMode']; + } + + + + /** + * Sets path to temporary directory. + * @return Configurator provides a fluent interface + */ + public function setTempDirectory($path) + { + $this->parameters['tempDir'] = $path; + if (($cacheDir = $this->getCacheDirectory()) && !is_dir($cacheDir)) { + mkdir($cacheDir, 0777); + } + return $this; + } + + + + /** + * Adds new parameters. The %params% will be expanded. + * @return Configurator provides a fluent interface + */ + public function addParameters(array $params) + { + $this->parameters = Helpers::merge($params, $this->parameters); + return $this; + } + + + + /** + * @return array + */ + protected function getDefaultParameters() + { + $trace = /*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE); + $debugMode = static::detectDebugMode(); + return array( + 'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : NULL, + 'wwwDir' => isset($_SERVER['SCRIPT_FILENAME']) ? dirname($_SERVER['SCRIPT_FILENAME']) : NULL, + 'debugMode' => $debugMode, + 'productionMode' => !$debugMode, + 'environment' => $debugMode ? self::DEVELOPMENT : self::PRODUCTION, + 'consoleMode' => PHP_SAPI === 'cli', + 'container' => array( + 'class' => 'SystemContainer', + 'parent' => 'Nette\DI\Container', + ) + ); + } + + + + /** + * @param string error log directory + * @param string administrator email + * @return void + */ + public function enableDebugger($logDirectory = NULL, $email = NULL) + { + Nette\Diagnostics\Debugger::$strictMode = TRUE; + Nette\Diagnostics\Debugger::enable($this->parameters['productionMode'], $logDirectory, $email); + } + + + + /** + * @return Nette\Loaders\RobotLoader + */ + public function createRobotLoader() + { + if (!($cacheDir = $this->getCacheDirectory())) { + throw new Nette\InvalidStateException("Set path to temporary directory using setTempDirectory()."); + } + $loader = new Nette\Loaders\RobotLoader; + $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage($cacheDir)); + $loader->autoRebuild = !$this->parameters['productionMode']; + return $loader; + } + + + + /** + * Adds configuration file. + * @return Configurator provides a fluent interface + */ + public function addConfig($file, $section = self::AUTO) + { + $this->files[] = array($file, $section === self::AUTO ? $this->parameters['environment'] : $section); + return $this; + } + + + + /** @deprecated */ + public function loadConfig($file, $section = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use addConfig(file, [section])->createContainer() instead.', E_USER_WARNING); + return $this->addConfig($file, $section)->createContainer(); + } + + + + /** + * Returns system DI container. + * @return \SystemContainer + */ + public function createContainer() + { + if ($cacheDir = $this->getCacheDirectory()) { + $cache = new Cache(new Nette\Caching\Storages\PhpFileStorage($cacheDir), 'Nette.Configurator'); + $cacheKey = array($this->parameters, $this->files); + $cached = $cache->load($cacheKey); + if (!$cached) { + $code = $this->buildContainer($dependencies); + $cache->save($cacheKey, $code, array( + Cache::FILES => $dependencies, + )); + $cached = $cache->load($cacheKey); + } + Nette\Utils\LimitedScope::load($cached['file'], TRUE); + + } elseif ($this->files) { + throw new Nette\InvalidStateException("Set path to temporary directory using setTempDirectory()."); + + } else { + Nette\Utils\LimitedScope::evaluate($this->buildContainer()); // back compatibility with Environment + } + + $container = new $this->parameters['container']['class']; + $container->initialize(); + Nette\Environment::setContext($container); // back compatibility + return $container; + } + + + + /** + * Build system container class. + * @return string + */ + protected function buildContainer(& $dependencies = NULL) + { + $loader = $this->createLoader(); + $config = array(); + $code = "files as $tmp) { + list($file, $section) = $tmp; + $config = Helpers::merge($loader->load($file, $section), $config); + $code .= "// source: $file $section\n"; + } + $code .= "\n"; + + $this->checkCompatibility($config); + + if (!isset($config['parameters'])) { + $config['parameters'] = array(); + } + $config['parameters'] = Helpers::merge($config['parameters'], $this->parameters); + + $compiler = $this->createCompiler(); + $this->onCompile($this, $compiler); + + $code .= $compiler->compile( + $config, + $this->parameters['container']['class'], + $config['parameters']['container']['parent'] + ); + $dependencies = array_merge($loader->getDependencies(), $this->isDebugMode() ? $compiler->getContainerBuilder()->getDependencies() : array()); + return $code; + } + + + + protected function checkCompatibility(array $config) + { + foreach (array('service' => 'services', 'variable' => 'parameters', 'variables' => 'parameters', 'mode' => 'parameters', 'const' => 'constants') as $old => $new) { + if (isset($config[$old])) { + throw new Nette\DeprecatedException("Section '$old' in configuration file is deprecated; use '$new' instead."); + } + } + if (isset($config['services'])) { + foreach ($config['services'] as $key => $def) { + foreach (array('option' => 'arguments', 'methods' => 'setup') as $old => $new) { + if (is_array($def) && isset($def[$old])) { + throw new Nette\DeprecatedException("Section '$old' in service definition is deprecated; refactor it into '$new'."); + } + } + } + } + } + + + + /** + * @return Compiler + */ + protected function createCompiler() + { + $compiler = new Compiler; + $compiler->addExtension('php', new Extensions\PhpExtension) + ->addExtension('constants', new Extensions\ConstantsExtension) + ->addExtension('nette', new Extensions\NetteExtension); + return $compiler; + } + + + + /** + * @return Loader + */ + protected function createLoader() + { + return new Loader; + } + + + + protected function getCacheDirectory() + { + return empty($this->parameters['tempDir']) ? NULL : $this->parameters['tempDir'] . '/cache'; + } + + + + /********************* tools ****************d*g**/ + + + + /** + * Detects debug mode by IP address. + * @param string|array IP addresses or computer names whitelist detection + * @return bool + */ + public static function detectDebugMode($list = NULL) + { + $list = is_string($list) ? preg_split('#[,\s]+#', $list) : (array) $list; + if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $list[] = '127.0.0.1'; + $list[] = '::1'; + } + return in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE); + } + + + + /** @deprecated */ + public function setProductionMode($value = TRUE) + { + return $this->setDebugMode(is_bool($value) ? !$value : $value); + } + + + + /** @deprecated */ + public function isProductionMode() + { + return !$this->isDebugMode(); + } + + + + /** @deprecated */ + public static function detectProductionMode($list = NULL) + { + return !static::detectDebugMode($list); + } + +} diff --git a/libs/Nette/Config/Extensions/ConstantsExtension.php b/libs/Nette/Config/Extensions/ConstantsExtension.php new file mode 100644 index 0000000..fe954d0 --- /dev/null +++ b/libs/Nette/Config/Extensions/ConstantsExtension.php @@ -0,0 +1,34 @@ +getConfig() as $name => $value) { + $class->methods['initialize']->addBody('define(?, ?);', array($name, $value)); + } + } + +} diff --git a/libs/Nette/Config/Extensions/NetteExtension.php b/libs/Nette/Config/Extensions/NetteExtension.php new file mode 100644 index 0000000..d26a5fc --- /dev/null +++ b/libs/Nette/Config/Extensions/NetteExtension.php @@ -0,0 +1,371 @@ + TRUE, + 'session' => array( + 'iAmUsingBadHost' => NULL, + 'autoStart' => 'smart', // true|false|smart + 'expiration' => NULL, + ), + 'application' => array( + 'debugger' => TRUE, + 'errorPresenter' => NULL, + 'catchExceptions' => '%productionMode%', + ), + 'routing' => array( + 'debugger' => TRUE, + 'routes' => array(), // of [mask => action] + ), + 'security' => array( + 'debugger' => TRUE, + 'frames' => 'SAMEORIGIN', // X-Frame-Options + 'users' => array(), // of [user => password] + 'roles' => array(), // of [role => parents] + 'resources' => array(), // of [resource => parents] + ), + 'mailer' => array( + 'smtp' => FALSE, + ), + 'database' => array(), // of [name => dsn, user, password, debugger, explain, autowired, reflection] + 'forms' => array( + 'messages' => array(), + ), + 'container' => array( + 'debugger' => FALSE, + ), + 'debugger' => array( + 'email' => NULL, + 'editor' => NULL, + 'browser' => NULL, + 'strictMode' => NULL, + 'bar' => array(), // of class name + 'blueScreen' => array(), // of callback + ), + ); + + public $databaseDefaults = array( + 'dsn' => NULL, + 'user' => NULL, + 'password' => NULL, + 'options' => NULL, + 'debugger' => TRUE, + 'explain' => TRUE, + 'reflection' => 'Nette\Database\Reflection\DiscoveredReflection', + ); + + + + public function loadConfiguration() + { + $container = $this->getContainerBuilder(); + $config = $this->getConfig($this->defaults); + + + // cache + $container->addDefinition($this->prefix('cacheJournal')) + ->setClass('Nette\Caching\Storages\FileJournal', array('%tempDir%')); + + $container->addDefinition('cacheStorage') // no namespace for back compatibility + ->setClass('Nette\Caching\Storages\FileStorage', array('%tempDir%/cache')); + + $container->addDefinition($this->prefix('templateCacheStorage')) + ->setClass('Nette\Caching\Storages\PhpFileStorage', array('%tempDir%/cache')) + ->setAutowired(FALSE); + + $container->addDefinition($this->prefix('cache')) + ->setClass('Nette\Caching\Cache', array(1 => '%namespace%')) + ->setParameters(array('namespace' => NULL)); + + + // http + $container->addDefinition($this->prefix('httpRequestFactory')) + ->setClass('Nette\Http\RequestFactory') + ->addSetup('setEncoding', array('UTF-8')) + ->setInternal(TRUE); + + $container->addDefinition('httpRequest') // no namespace for back compatibility + ->setClass('Nette\Http\Request') + ->setFactory('@Nette\Http\RequestFactory::createHttpRequest'); + + $container->addDefinition('httpResponse') // no namespace for back compatibility + ->setClass('Nette\Http\Response'); + + $container->addDefinition($this->prefix('httpContext')) + ->setClass('Nette\Http\Context'); + + + // session + $session = $container->addDefinition('session') // no namespace for back compatibility + ->setClass('Nette\Http\Session'); + + if (isset($config['session']['expiration'])) { + $session->addSetup('setExpiration', array($config['session']['expiration'])); + } + if (isset($config['session']['iAmUsingBadHost'])) { + $session->addSetup('Nette\Framework::$iAmUsingBadHost = ?;', array((bool) $config['session']['iAmUsingBadHost'])); + } + unset($config['session']['expiration'], $config['session']['autoStart'], $config['session']['iAmUsingBadHost']); + if (!empty($config['session'])) { + $session->addSetup('setOptions', array($config['session'])); + } + + + // security + $container->addDefinition($this->prefix('userStorage')) + ->setClass('Nette\Http\UserStorage'); + + $user = $container->addDefinition('user') // no namespace for back compatibility + ->setClass('Nette\Security\User'); + + if (!$container->parameters['productionMode'] && $config['security']['debugger']) { + $user->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array( + new Nette\DI\Statement('Nette\Security\Diagnostics\UserPanel') + )); + } + + if ($config['security']['users']) { + $container->addDefinition($this->prefix('authenticator')) + ->setClass('Nette\Security\SimpleAuthenticator', array($config['security']['users'])); + } + + if ($config['security']['roles'] || $config['security']['resources']) { + $authorizator = $container->addDefinition($this->prefix('authorizator')) + ->setClass('Nette\Security\Permission'); + foreach ($config['security']['roles'] as $role => $parents) { + $authorizator->addSetup('addRole', array($role, $parents)); + } + foreach ($config['security']['resources'] as $resource => $parents) { + $authorizator->addSetup('addResource', array($resource, $parents)); + } + } + + + // application + $application = $container->addDefinition('application') // no namespace for back compatibility + ->setClass('Nette\Application\Application') + ->addSetup('$catchExceptions', $config['application']['catchExceptions']) + ->addSetup('$errorPresenter', $config['application']['errorPresenter']); + + if ($config['application']['debugger']) { + $application->addSetup('Nette\Application\Diagnostics\RoutingPanel::initializePanel'); + } + + $container->addDefinition($this->prefix('presenterFactory')) + ->setClass('Nette\Application\PresenterFactory', array( + isset($container->parameters['appDir']) ? $container->parameters['appDir'] : NULL + )); + + + // routing + $router = $container->addDefinition('router') // no namespace for back compatibility + ->setClass('Nette\Application\Routers\RouteList'); + + foreach ($config['routing']['routes'] as $mask => $action) { + $router->addSetup('$service[] = new Nette\Application\Routers\Route(?, ?);', array($mask, $action)); + } + + if (!$container->parameters['productionMode'] && $config['routing']['debugger']) { + $application->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array( + new Nette\DI\Statement('Nette\Application\Diagnostics\RoutingPanel') + )); + } + + + // mailer + if (empty($config['mailer']['smtp'])) { + $container->addDefinition($this->prefix('mailer')) + ->setClass('Nette\Mail\SendmailMailer'); + } else { + $container->addDefinition($this->prefix('mailer')) + ->setClass('Nette\Mail\SmtpMailer', array($config['mailer'])); + } + + $container->addDefinition($this->prefix('mail')) + ->setClass('Nette\Mail\Message') + ->addSetup('setMailer') + ->setShared(FALSE); + + + // forms + $container->addDefinition($this->prefix('basicForm')) + ->setClass('Nette\Forms\Form') + ->setShared(FALSE); + + + // templating + $latte = $container->addDefinition($this->prefix('latte')) + ->setClass('Nette\Latte\Engine') + ->setShared(FALSE); + + if (empty($config['xhtml'])) { + $latte->addSetup('$service->getCompiler()->defaultContentType = ?', Nette\Latte\Compiler::CONTENT_HTML); + } + + $container->addDefinition($this->prefix('template')) + ->setClass('Nette\Templating\FileTemplate') + ->addSetup('registerFilter', array($latte)) + ->addSetup('registerHelperLoader', array('Nette\Templating\Helpers::loader')) + ->setShared(FALSE); + + + // database + $container->addDefinition($this->prefix('database')) + ->setClass('Nette\DI\NestedAccessor', array('@container', $this->prefix('database'))); + + if (isset($config['database']['dsn'])) { + $config['database'] = array('default' => $config['database']); + } + + $autowired = TRUE; + foreach ((array) $config['database'] as $name => $info) { + if (!is_array($info)) { + continue; + } + $info += $this->databaseDefaults + array('autowired' => $autowired); + $autowired = FALSE; + + foreach ((array) $info['options'] as $key => $value) { + unset($info['options'][$key]); + $info['options'][constant($key)] = $value; + } + + $connection = $container->addDefinition($this->prefix("database.$name")) + ->setClass('Nette\Database\Connection', array($info['dsn'], $info['user'], $info['password'], $info['options'])) + ->setAutowired($info['autowired']) + ->addSetup('setCacheStorage') + ->addSetup('Nette\Diagnostics\Debugger::$blueScreen->addPanel(?)', array( + 'Nette\Database\Diagnostics\ConnectionPanel::renderException' + )); + + if ($info['reflection']) { + $connection->addSetup('setDatabaseReflection', is_string($info['reflection']) + ? array(new Nette\DI\Statement(preg_match('#^[a-z]+$#', $info['reflection']) ? 'Nette\Database\Reflection\\' . ucfirst($info['reflection']) . 'Reflection' : $info['reflection'])) + : Nette\Config\Compiler::filterArguments(array($info['reflection'])) + ); + } + + if (!$container->parameters['productionMode'] && $info['debugger']) { + $panel = $container->addDefinition($this->prefix("database.{$name}ConnectionPanel")) + ->setClass('Nette\Database\Diagnostics\ConnectionPanel') + ->setAutowired(FALSE) + ->addSetup('$explain', !empty($info['explain'])) + ->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array('@self')); + + $connection->addSetup('$service->onQuery[] = ?', array(array($panel, 'logQuery'))); + } + } + } + + + + public function afterCompile(Nette\Utils\PhpGenerator\ClassType $class) + { + $initialize = $class->methods['initialize']; + $container = $this->getContainerBuilder(); + $config = $this->getConfig($this->defaults); + + // debugger + foreach (array('email', 'editor', 'browser', 'strictMode', 'maxLen', 'maxDepth') as $key) { + if (isset($config['debugger'][$key])) { + $initialize->addBody('Nette\Diagnostics\Debugger::$? = ?;', array($key, $config['debugger'][$key])); + } + } + + if (!$container->parameters['productionMode']) { + if ($config['container']['debugger']) { + $config['debugger']['bar'][] = 'Nette\DI\Diagnostics\ContainerPanel'; + } + + foreach ((array) $config['debugger']['bar'] as $item) { + $initialize->addBody($container->formatPhp( + 'Nette\Diagnostics\Debugger::$bar->addPanel(?);', + Nette\Config\Compiler::filterArguments(array(is_string($item) ? new Nette\DI\Statement($item) : $item)) + )); + } + + foreach ((array) $config['debugger']['blueScreen'] as $item) { + $initialize->addBody($container->formatPhp( + 'Nette\Diagnostics\Debugger::$blueScreen->addPanel(?);', + Nette\Config\Compiler::filterArguments(array($item)) + )); + } + } + + if (!empty($container->parameters['tempDir'])) { + $initialize->addBody($this->checkTempDir($container->expand('%tempDir%/cache'))); + } + + foreach ((array) $config['forms']['messages'] as $name => $text) { + $initialize->addBody('Nette\Forms\Rules::$defaultMessages[Nette\Forms\Form::?] = ?;', array($name, $text)); + } + + if ($config['session']['autoStart'] === 'smart') { + $initialize->addBody('$this->session->exists() && $this->session->start();'); + } elseif ($config['session']['autoStart']) { + $initialize->addBody('$this->session->start();'); + } + + if (empty($config['xhtml'])) { + $initialize->addBody('Nette\Utils\Html::$xhtml = ?;', array((bool) $config['xhtml'])); + } + + if (isset($config['security']['frames']) && $config['security']['frames'] !== TRUE) { + $frames = $config['security']['frames']; + if ($frames === FALSE) { + $frames = 'DENY'; + } elseif (preg_match('#^https?:#', $frames)) { + $frames = "ALLOW-FROM $frames"; + } + $initialize->addBody('header(?);', array("X-Frame-Options: $frames")); + } + + foreach ($container->findByTag('run') as $name => $on) { + if ($on) { + $initialize->addBody('$this->getService(?);', array($name)); + } + } + } + + + + private function checkTempDir($dir) + { + // checks whether directory is writable + $uniq = uniqid('_', TRUE); + if (!@mkdir("$dir/$uniq", 0777)) { // @ - is escalated to exception + throw new Nette\InvalidStateException("Unable to write to directory '$dir'. Make this directory writable."); + } + + // tests subdirectory mode + $useDirs = @file_put_contents("$dir/$uniq/_", '') !== FALSE; // @ - error is expected + @unlink("$dir/$uniq/_"); + @rmdir("$dir/$uniq"); // @ - directory may not already exist + + return 'Nette\Caching\Storages\FileStorage::$useDirectories = ' . ($useDirs ? 'TRUE' : 'FALSE') . ";\n"; + } + +} diff --git a/libs/Nette/Config/Extensions/PhpExtension.php b/libs/Nette/Config/Extensions/PhpExtension.php new file mode 100644 index 0000000..12580b9 --- /dev/null +++ b/libs/Nette/Config/Extensions/PhpExtension.php @@ -0,0 +1,55 @@ +methods['initialize']; + foreach ($this->getConfig() as $name => $value) { + if (!is_scalar($value)) { + throw new Nette\InvalidStateException("Configuration value for directive '$name' is not scalar."); + + } elseif ($name === 'include_path') { + $initialize->addBody('set_include_path(?);', array(str_replace(';', PATH_SEPARATOR, $value))); + + } elseif ($name === 'ignore_user_abort') { + $initialize->addBody('ignore_user_abort(?);', array($value)); + + } elseif ($name === 'max_execution_time') { + $initialize->addBody('set_time_limit(?);', array($value)); + + } elseif ($name === 'date.timezone') { + $initialize->addBody('date_default_timezone_set(?);', array($value)); + + } elseif (function_exists('ini_set')) { + $initialize->addBody('ini_set(?, ?);', array($name, $value)); + + } elseif (ini_get($name) != $value) { // intentionally == + throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); + } + } + } + +} diff --git a/libs/Nette/Config/Helpers.php b/libs/Nette/Config/Helpers.php new file mode 100644 index 0000000..2744059 --- /dev/null +++ b/libs/Nette/Config/Helpers.php @@ -0,0 +1,95 @@ + $val) { + if (is_int($key)) { + $right[] = $val; + } else { + if (is_array($val) && isset($val[self::EXTENDS_KEY])) { + if ($val[self::EXTENDS_KEY] === self::OVERWRITE) { + unset($val[self::EXTENDS_KEY]); + } + } elseif (isset($right[$key])) { + $val = static::merge($val, $right[$key]); + } + $right[$key] = $val; + } + } + return $right; + + } elseif ($left === NULL && is_array($right)) { + return $right; + + } else { + return $left; + } + } + + + + /** + * Finds out and removes information about the parent. + * @return mixed + */ + public static function takeParent(& $data) + { + if (is_array($data) && isset($data[self::EXTENDS_KEY])) { + $parent = $data[self::EXTENDS_KEY]; + unset($data[self::EXTENDS_KEY]); + return $parent; + } + } + + + + /** + * @return bool + */ + public static function isOverwriting(& $data) + { + return is_array($data) && isset($data[self::EXTENDS_KEY]) && $data[self::EXTENDS_KEY] === self::OVERWRITE; + } + + + + /** + * @return bool + */ + public static function isInheriting(& $data) + { + return is_array($data) && isset($data[self::EXTENDS_KEY]) && $data[self::EXTENDS_KEY] !== self::OVERWRITE; + } + +} diff --git a/libs/Nette/Config/IAdapter.php b/libs/Nette/Config/IAdapter.php index 3b1edf1..0242ca5 100644 --- a/libs/Nette/Config/IAdapter.php +++ b/libs/Nette/Config/IAdapter.php @@ -1,41 +1,40 @@ - key2> key3) */ - public static $keySeparator = '.'; - - /** @var string section inheriting separator (section < parent) */ - public static $sectionSeparator = ' < '; - - /** @var string raw section marker */ - public static $rawSection = '!'; - - - - /** - * Static class - cannot be instantiated. - */ - final public function __construct() - { - throw new Nette\StaticClassException; - } - - - - /** - * Reads configuration from INI file. - * @param string file name - * @return array - * @throws Nette\InvalidStateException - */ - public static function load($file) - { - if (!is_file($file) || !is_readable($file)) { - throw new Nette\FileNotFoundException("File '$file' is missing or is not readable."); - } - - Nette\Diagnostics\Debugger::tryError(); - $ini = parse_ini_file($file, TRUE); - if (Nette\Diagnostics\Debugger::catchError($e)) { - throw new Nette\InvalidStateException('parse_ini_file(): ' . $e->getMessage(), 0, $e); - } - - $separator = trim(self::$sectionSeparator); - $data = array(); - foreach ($ini as $secName => $secData) { - // is section? - if (is_array($secData)) { - if (substr($secName, -1) === self::$rawSection) { - $secName = substr($secName, 0, -1); - - } elseif (self::$keySeparator) { - // process key separators (key1> key2> key3) - $tmp = array(); - foreach ($secData as $key => $val) { - $cursor = & $tmp; - foreach (explode(self::$keySeparator, $key) as $part) { - if (!isset($cursor[$part]) || is_array($cursor[$part])) { - $cursor = & $cursor[$part]; - } else { - throw new Nette\InvalidStateException("Invalid key '$key' in section [$secName] in file '$file'."); - } - } - $cursor = $val; - } - $secData = $tmp; - } - - // process extends sections like [staging < production] (with special support for separator ':') - $parts = $separator ? explode($separator, strtr($secName, ':', $separator)) : array($secName); - if (count($parts) > 1) { - $parent = trim($parts[1]); - if (!isset($data[$parent]) || !is_array($data[$parent])) { - throw new Nette\InvalidStateException("Missing parent section [$parent] in file '$file'."); - } - $secData = Nette\Utils\Arrays::mergeTree($secData, $data[$parent]); - $secName = trim($parts[0]); - if ($secName === '') { - throw new Nette\InvalidStateException("Invalid empty section name in file '$file'."); - } - } - } - - if (self::$keySeparator) { - $cursor = & $data; - foreach (explode(self::$keySeparator, $secName) as $part) { - if (!isset($cursor[$part]) || is_array($cursor[$part])) { - $cursor = & $cursor[$part]; - } else { - throw new Nette\InvalidStateException("Invalid section [$secName] in file '$file'."); - } - } - } else { - $cursor = & $data[$secName]; - } - - if (is_array($secData) && is_array($cursor)) { - $secData = Nette\Utils\Arrays::mergeTree($secData, $cursor); - } - - $cursor = $secData; - } - - return $data; - } - - - - /** - * Write INI file. - * @param mixed - * @param string file - * @return void - */ - public static function save($config, $file) - { - $output = array(); - $output[] = '; generated by Nette';// at ' . @strftime('%c'); - $output[] = ''; - - foreach ($config as $secName => $secData) { - if (!(is_array($secData) || $secData instanceof \Traversable)) { - throw new Nette\InvalidStateException("Invalid section '$secName'."); - } - - $output[] = "[$secName]"; - self::build($secData, $output, ''); - $output[] = ''; - } - - if (!file_put_contents($file, implode(PHP_EOL, $output))) { - throw new Nette\IOException("Cannot write file '$file'."); - } - } - - - - /** - * Recursive builds INI list. - * @param array|\Traversable - * @param array - * @param string - * @return void - */ - private static function build($input, & $output, $prefix) - { - foreach ($input as $key => $val) { - if (is_array($val) || $val instanceof \Traversable) { - self::build($val, $output, $prefix . $key . self::$keySeparator); - - } elseif (is_bool($val)) { - $output[] = "$prefix$key = " . ($val ? 'true' : 'false'); - - } elseif (is_numeric($val)) { - $output[] = "$prefix$key = $val"; - - } elseif (is_string($val)) { - $output[] = "$prefix$key = \"$val\""; - - } else { - throw new Nette\InvalidArgumentException("The '$prefix$key' item must be scalar or array, " . gettype($val) ." given."); - } - } - } - -} diff --git a/libs/Nette/Config/Loader.php b/libs/Nette/Config/Loader.php new file mode 100644 index 0000000..77c5802 --- /dev/null +++ b/libs/Nette/Config/Loader.php @@ -0,0 +1,139 @@ + 'Nette\Config\Adapters\PhpAdapter', + 'ini' => 'Nette\Config\Adapters\IniAdapter', + 'neon' => 'Nette\Config\Adapters\NeonAdapter', + ); + + private $dependencies = array(); + + + + /** + * Reads configuration from file. + * @param string file name + * @param string optional section to load + * @return array + */ + public function load($file, $section = NULL) + { + if (!is_file($file) || !is_readable($file)) { + throw new Nette\FileNotFoundException("File '$file' is missing or is not readable."); + } + $this->dependencies[] = $file = realpath($file); + $data = $this->getAdapter($file)->load($file); + + if ($section) { + if (isset($data[self::INCLUDES_KEY])) { + throw new Nette\InvalidStateException("Section 'includes' must be placed under some top section in file '$file'."); + } + $data = $this->getSection($data, $section, $file); + } + + // include child files + $merged = array(); + if (isset($data[self::INCLUDES_KEY])) { + Validators::assert($data[self::INCLUDES_KEY], 'list', "section 'includes' in file '$file'"); + foreach ($data[self::INCLUDES_KEY] as $include) { + $merged = Helpers::merge($this->load(dirname($file) . '/' . $include), $merged); + } + } + unset($data[self::INCLUDES_KEY]); + + return Helpers::merge($data, $merged); + } + + + + /** + * Save configuration to file. + * @param array + * @param string file + * @return void + */ + public function save($data, $file) + { + if (file_put_contents($file, $this->getAdapter($file)->dump($data)) === FALSE) { + throw new Nette\IOException("Cannot write file '$file'."); + } + } + + + + /** + * Returns configuration files. + * @return array + */ + public function getDependencies() + { + return array_unique($this->dependencies); + } + + + + /** + * Registers adapter for given file extension. + * @param string file extension + * @param string|Nette\Config\IAdapter + * @return Loader provides a fluent interface + */ + public function addAdapter($extension, $adapter) + { + $this->adapters[strtolower($extension)] = $adapter; + return $this; + } + + + + /** @return IAdapter */ + private function getAdapter($file) + { + $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + if (!isset($this->adapters[$extension])) { + throw new Nette\InvalidArgumentException("Unknown file extension '$file'."); + } + return is_object($this->adapters[$extension]) ? $this->adapters[$extension] : new $this->adapters[$extension]; + } + + + + private function getSection(array $data, $key, $file) + { + Validators::assertField($data, $key, 'array|null', "section '%' in file '$file'"); + $item = $data[$key]; + if ($parent = Helpers::takeParent($item)) { + $item = Helpers::merge($item, $this->getSection($data, $parent, $file)); + } + return $item; + } + +} diff --git a/libs/Nette/Config/NeonAdapter.php b/libs/Nette/Config/NeonAdapter.php deleted file mode 100644 index d1e2295..0000000 --- a/libs/Nette/Config/NeonAdapter.php +++ /dev/null @@ -1,98 +0,0 @@ - $secData) { - if ($secData === NULL) { // empty section - $secData = array(); - } - - if (is_array($secData)) { - // process extends sections like [staging < production] - $parts = $separator ? explode($separator, $secName) : array($secName); - if (count($parts) > 1) { - $parent = trim($parts[1]); - if (!isset($data[$parent]) || !is_array($data[$parent])) { - throw new Nette\InvalidStateException("Missing parent section '$parent' in file '$file'."); - } - $secData = Nette\Utils\Arrays::mergeTree($secData, $data[$parent]); - $secName = trim($parts[0]); - if ($secName === '') { - throw new Nette\InvalidStateException("Invalid empty section name in file '$file'."); - } - } - } - - $data[$secName] = $secData; - } - - return $data; - } - - - - /** - * Write NEON file. - * @param mixed - * @param string file - * @return void - */ - public static function save($config, $file) - { - if (!file_put_contents($file, "# generated by Nette\n\n" . Neon::encode($config, Neon::BLOCK))) { - throw new Nette\IOException("Cannot write file '$file'."); - } - } - -} diff --git a/libs/Nette/DI/Container.php b/libs/Nette/DI/Container.php index 5e5111f..553e490 100644 --- a/libs/Nette/DI/Container.php +++ b/libs/Nette/DI/Container.php @@ -1,322 +1,371 @@ -updating(); - if (!is_string($name) || $name === '') { - throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); - } - - if (isset($this->registry[$name]) || method_exists($this, "createService$name")) { - throw new Nette\InvalidStateException("Service '$name' has already been registered."); - } - - if (is_string($tags)) { - $tags = array(self::TAG_TYPEHINT => array($tags)); - } elseif (is_array($tags)) { - foreach ($tags as $id => $attrs) { - if (is_int($id) && is_string($attrs)) { - $tags[$attrs] = array(); - unset($tags[$id]); - } elseif (!is_array($attrs)) { - $tags[$id] = (array) $attrs; - } - } - } - - if (is_string($service) && strpos($service, ':') === FALSE) { // class name - if (!isset($tags[self::TAG_TYPEHINT][0])) { - $tags[self::TAG_TYPEHINT][0] = $service; - } - $service = new ServiceBuilder($service); - } - - if ($service instanceof IServiceBuilder) { - $factory = array($service, 'createService'); - - } elseif (is_object($service) && !$service instanceof \Closure && !$service instanceof Nette\Callback) { - $this->registry[$name] = $service; - $this->tags[$name] = $tags; - return $this; - - } else { - $factory = $service; - } - - $this->factories[$name] = array(callback($factory)); - $this->tags[$name] = $tags; - $this->registry[$name] = & $this->factories[$name][1]; // forces cloning using reference - return $service; - } - - - - /** - * Removes the specified service type from the container. - * @return void - */ - public function removeService($name) - { - $this->updating(); - unset($this->registry[$name], $this->factories[$name]); - } - - - - /** - * Gets the service object by name. - * @param string - * @return object - */ - public function getService($name) - { - if (isset($this->registry[$name])) { - return $this->registry[$name]; - - } elseif (isset($this->creating[$name])) { - throw new Nette\InvalidStateException("Circular reference detected for services: " - . implode(', ', array_keys($this->creating)) . "."); - } - - if (isset($this->factories[$name])) { - list($factory) = $this->factories[$name]; - if (!$factory->isCallable()) { - throw new Nette\InvalidStateException("Unable to create service '$name', factory '$factory' is not callable."); - } - - $this->creating[$name] = TRUE; - try { - $service = $factory($this); - } catch (\Exception $e) {} - - } elseif (method_exists($this, $factory = 'createService' . ucfirst($name))) { // static method - $this->creating[$name] = TRUE; - try { - $service = $this->$factory(); - } catch (\Exception $e) {} - - } else { - throw new MissingServiceException("Service '$name' not found."); - } - - unset($this->creating[$name]); - - if (isset($e)) { - throw $e; - - } elseif (!is_object($service)) { - throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not object."); - - } elseif (isset($this->tags[$name][self::TAG_TYPEHINT][0]) && !$service instanceof $this->tags[$name][self::TAG_TYPEHINT][0]) { - throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not '{$this->tags[$name][self::TAG_TYPEHINT][0]}' type."); - } - - unset($this->factories[$name]); - return $this->registry[$name] = $service; - } - - - - /** - * Gets the service object of the specified type. - * @param string - * @return object - */ - public function getServiceByType($type) - { - foreach ($this->registry as $name => $service) { - if (isset($this->tags[$name][self::TAG_TYPEHINT][0]) ? !strcasecmp($this->tags[$name][self::TAG_TYPEHINT][0], $type) : $service instanceof $type) { - $found[] = $name; - } - } - if (!isset($found)) { - throw new MissingServiceException("Service matching '$type' type not found."); - - } elseif (count($found) > 1) { - throw new AmbiguousServiceException("Found more than one service ('" . implode("', '", $found) . "') matching '$type' type."); - } - return $this->getService($found[0]); - } - - - - /** - * Gets the service objects of the specified tag. - * @param string - * @return array of [service name => tag attributes] - */ - public function getServiceNamesByTag($tag) - { - $found = array(); - foreach ($this->registry as $name => $service) { - if (isset($this->tags[$name][$tag])) { - $found[$name] = $this->tags[$name][$tag]; - } - } - return $found; - } - - - - /** - * Exists the service? - * @param string service name - * @return bool - */ - public function hasService($name) - { - return isset($this->registry[$name]) - || isset($this->factories[$name]) - || method_exists($this, "createService$name"); - } - - - - /** - * Checks the service type. - * @param string - * @param string - * @return bool - */ - public function checkServiceType($name, $type) - { - return isset($this->tags[$name][self::TAG_TYPEHINT][0]) - ? !strcasecmp($this->tags[$name][self::TAG_TYPEHINT][0], $type) - : (isset($this->registry[$name]) && $this->registry[$name] instanceof $type); - } - - - - /********************* tools ****************d*g**/ - - - - /** - * Expands %placeholders% in string. - * @param string - * @return string - * @throws Nette\InvalidStateException - */ - public function expand($s) - { - if (is_string($s) && strpos($s, '%') !== FALSE) { - $that = $this; - return @preg_replace_callback('#%([a-z0-9._-]*)%#i', function ($m) use ($that) { // intentionally @ due PHP bug #39257 - list(, $param) = $m; - if ($param === '') { - return '%'; - } elseif (!isset($that->params[$param])) { - throw new Nette\InvalidArgumentException("Missing parameter '$param'."); - } elseif (!is_scalar($val = $that->params[$param])) { - throw new Nette\InvalidStateException("Parameter '$param' is not scalar."); - } - return $val; - }, $s); - } - return $s; - } - - - - /********************* shortcuts ****************d*g**/ - - - - /** - * Gets the service object, shortcut for getService(). - * @param string - * @return object - */ - public function &__get($name) - { - if (!isset($this->registry[$name])) { - $this->getService($name); - } - return $this->registry[$name]; - } - - - - /** - * Adds the service, shortcut for addService(). - * @param string - * @param object - * @return void - */ - public function __set($name, $value) - { - $this->addService($name, $value); - } - - - - /** - * Exists the service? - * @param string - * @return bool - */ - public function __isset($name) - { - return $this->hasService($name); - } - - - - /** - * Removes the service, shortcut for removeService(). - * @return void - */ - public function __unset($name) - { - $this->removeService($name); - } - -} +parameters = $params + $this->parameters; + $this->params = &$this->parameters; + } + + + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + + + /** + * Adds the service or service factory to the container. + * @param string + * @param mixed object, class name or callable + * @param array service meta information + * @return Container provides a fluent interface + */ + public function addService($name, $service, array $meta = NULL) + { + $this->updating(); + if (!is_string($name) || $name === '') { + throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); + } + + if (isset($this->registry[$name])) { + throw new Nette\InvalidStateException("Service '$name' has already been registered."); + } + + if (is_object($service) && !$service instanceof \Closure && !$service instanceof Nette\Callback) { + $this->registry[$name] = $service; + $this->meta[$name] = $meta; + return $this; + + } elseif (!is_string($service) || strpos($service, ':') !== FALSE/*5.2* || $service[0] === "\0"*/) { // callable + $service = new Nette\Callback($service); + } + + $this->factories[$name] = array($service); + $this->registry[$name] = & $this->factories[$name][1]; // forces cloning using reference + $this->meta[$name] = $meta; + return $this; + } + + + + /** + * Removes the service from the container. + * @param string + * @return void + */ + public function removeService($name) + { + $this->updating(); + unset($this->registry[$name], $this->factories[$name], $this->meta[$name]); + } + + + + /** + * Gets the service object by name. + * @param string + * @return object + */ + public function getService($name) + { + if (isset($this->registry[$name])) { + return $this->registry[$name]; + + } elseif (isset($this->creating[$name])) { + throw new Nette\InvalidStateException("Circular reference detected for services: " + . implode(', ', array_keys($this->creating)) . "."); + } + + if (isset($this->factories[$name])) { + list($factory) = $this->factories[$name]; + if (is_string($factory)) { + if (!class_exists($factory)) { + throw new Nette\InvalidStateException("Cannot instantiate service, class '$factory' not found."); + } + try { + $this->creating[$name] = TRUE; + $service = new $factory; + } catch (\Exception $e) {} + + } elseif (!$factory->isCallable()) { + throw new Nette\InvalidStateException("Unable to create service '$name', factory '$factory' is not callable."); + + } else { + $this->creating[$name] = TRUE; + try { + $service = $factory/*5.2*->invoke*/($this); + } catch (\Exception $e) {} + } + + } elseif (method_exists($this, $factory = Container::getMethodName($name)) && $this->getReflection()->getMethod($factory)->getName() === $factory) { + $this->creating[$name] = TRUE; + try { + $service = $this->$factory(); + } catch (\Exception $e) {} + + } else { + throw new MissingServiceException("Service '$name' not found."); + } + + unset($this->creating[$name]); + + if (isset($e)) { + throw $e; + + } elseif (!is_object($service)) { + throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not object."); + } + + return $this->registry[$name] = $service; + } + + + + /** + * Does the service exist? + * @param string service name + * @return bool + */ + public function hasService($name) + { + return isset($this->registry[$name]) + || isset($this->factories[$name]) + || method_exists($this, $method = Container::getMethodName($name)) && $this->getReflection()->getMethod($method)->getName() === $method; + } + + + + /** + * Is the service created? + * @param string service name + * @return bool + */ + public function isCreated($name) + { + if (!$this->hasService($name)) { + throw new MissingServiceException("Service '$name' not found."); + } + return isset($this->registry[$name]); + } + + + + /** + * Resolves service by type. + * @param string class or interface + * @param bool throw exception if service doesn't exist? + * @return object service or NULL + * @throws MissingServiceException + */ + public function getByType($class, $need = TRUE) + { + $lower = ltrim(strtolower($class), '\\'); + if (!isset($this->classes[$lower])) { + if ($need) { + throw new MissingServiceException("Service of type $class not found."); + } + } elseif ($this->classes[$lower] === FALSE) { + throw new MissingServiceException("Multiple services of type $class found."); + } else { + return $this->getService($this->classes[$lower]); + } + } + + + + /** + * Gets the service names of the specified tag. + * @param string + * @return array of [service name => tag attributes] + */ + public function findByTag($tag) + { + $found = array(); + foreach ($this->meta as $name => $meta) { + if (isset($meta[self::TAGS][$tag])) { + $found[$name] = $meta[self::TAGS][$tag]; + } + } + return $found; + } + + + + /********************* autowiring ****************d*g**/ + + + + /** + * Creates new instance using autowiring. + * @param string class + * @param array arguments + * @return object + * @throws Nette\InvalidArgumentException + */ + public function createInstance($class, array $args = array()) + { + $rc = Nette\Reflection\ClassType::from($class); + if (!$rc->isInstantiable()) { + throw new ServiceCreationException("Class $class is not instantiable."); + + } elseif ($constructor = $rc->getConstructor()) { + return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this)); + + } elseif ($args) { + throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor."); + } + return new $class; + } + + + + /** + * Calls method using autowiring. + * @param mixed class, object, function, callable + * @param array arguments + * @return mixed + */ + public function callMethod($function, array $args = array()) + { + $callback = new Nette\Callback($function); + return $callback->invokeArgs(Helpers::autowireArguments($callback->toReflection(), $args, $this)); + } + + + + /********************* shortcuts ****************d*g**/ + + + + /** + * Expands %placeholders%. + * @param mixed + * @return mixed + */ + public function expand($s) + { + return Helpers::expand($s, $this->parameters); + } + + + + /** + * Gets the service object, shortcut for getService(). + * @param string + * @return object + */ + public function &__get($name) + { + if (!isset($this->registry[$name])) { + $this->getService($name); + } + return $this->registry[$name]; + } + + + + /** + * Adds the service object. + * @param string + * @param object + * @return void + */ + public function __set($name, $service) + { + $this->updating(); + if (!is_string($name) || $name === '') { + throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); + + } elseif (isset($this->registry[$name])) { + throw new Nette\InvalidStateException("Service '$name' has already been registered."); + + } elseif (!is_object($service)) { + throw new Nette\InvalidArgumentException("Service must be a object, " . gettype($service) . " given."); + } + $this->registry[$name] = $service; + } + + + + /** + * Does the service exist? + * @param string + * @return bool + */ + public function __isset($name) + { + return $this->hasService($name); + } + + + + /** + * Removes the service, shortcut for removeService(). + * @return void + */ + public function __unset($name) + { + $this->removeService($name); + } + + + + public static function getMethodName($name, $isService = TRUE) + { + $uname = ucfirst($name); + return ($isService ? 'createService' : 'create') . ($name === $uname ? '__' : '') . str_replace('.', '__', $uname); + } + +} diff --git a/libs/Nette/DI/ContainerBuilder.php b/libs/Nette/DI/ContainerBuilder.php index 36cdc3b..1ea9f65 100644 --- a/libs/Nette/DI/ContainerBuilder.php +++ b/libs/Nette/DI/ContainerBuilder.php @@ -1,148 +1,585 @@ - array( - * class => 'ClassName' or factory => 'Factory::create' - * arguments => array(...) - * methods => array( - * array(methodName, array(...)) - * ... - * ) - * tags => array(...) - * ) - */ - public function addDefinitions(IContainer $container, array $definitions) - { - foreach ($definitions as $name => $definition) { - if (!is_array($definition)) { - $definition = array('class' => $definition); - } - - $arguments = isset($definition['arguments']) ? $definition['arguments'] : array(); - $expander = function(&$val) use ($container) { - $val = $val[0] === '@' ? $container->getService(substr($val, 1)) : $container->expand($val); - }; - - if (isset($definition['class'])) { - $class = $definition['class']; - $methods = isset($definition['methods']) ? $definition['methods'] : array(); - $factory = function($container) use ($class, $arguments, $methods, $expander) { - $class = $container->expand($class); - if ($arguments) { - array_walk_recursive($arguments, $expander); - $service = Nette\Reflection\ClassType::from($class)->newInstanceArgs($arguments); - } else { - $service = new $class; - } - - array_walk_recursive($methods, $expander); - foreach ($methods as $method) { - call_user_func_array(array($service, $method[0]), isset($method[1]) ? $method[1] : array()); - } - - return $service; - }; - - } elseif (isset($definition['factory'])) { - array_unshift($arguments, $definition['factory']); - $factory = function($container) use ($arguments, $expander) { - array_walk_recursive($arguments, $expander); - $factory = $arguments[0]; $arguments[0] = $container; - return call_user_func_array($factory, $arguments); - }; - } else { - throw new Nette\InvalidStateException("Factory method is not specified."); - } - - if (isset($definition['tags'])) { - $tags = (array) $definition['tags']; - array_walk_recursive($tags, $expander); - } else { - $tags = NULL; - } - $container->addService($name, $factory, $tags); - } - } - - - - public function generateCode(array $definitions) - { - $code = ''; - foreach ($definitions as $name => $definition) { - $name = $this->varExport($name); - if (is_scalar($definition)) { - $factory = $this->varExport($definition); - $code .= "\$container->addService($name, $factory);\n\n"; - continue; - } - - $arguments = $this->argsExport(isset($definition['arguments']) ? $definition['arguments'] : array()); - - if (isset($definition['class'])) { - $class = $this->argsExport(array($definition['class'])); - $methods = isset($definition['methods']) ? $definition['methods'] : array(); - $factory = "function(\$container) {\n\t\$class = $class; \$service = new \$class($arguments);\n"; - foreach ($methods as $method) { - $args = isset($method[1]) ? $this->argsExport($method[1]) : ''; - $factory .= "\t\$service->$method[0]($args);\n"; - } - $factory .= "\treturn \$service;\n}"; - - } elseif (isset($definition['factory'])) { - $factory = $this->argsExport(array($definition['factory'])); - $factory = "function(\$container) {\n\treturn call_user_func(\n\t\t$factory,\n\t\t\$container" - . ($arguments ? ",\n\t\t$arguments" : '') . "\n\t);\n}"; - } else { - throw new Nette\InvalidStateException("Factory method is not specified."); - } - - $tags = isset($definition['tags']) ? $this->argsExport(array($definition['tags'])) : 'NULL'; - $code .= "\$container->addService($name, $factory, $tags);\n\n"; - } - return $code; - } - - - - private function argsExport($args) - { - $args = implode(', ', array_map(array($this, 'varExport'), $args)); - $args = preg_replace("#'@(\w+)'#", '\$container->getService(\'$1\')', $args); - $args = preg_replace("#('[^']*%[^']*')#", '\$container->expand($1)', $args); - return $args; - } - - - - private function varExport($arg) - { - return preg_replace('#\n *#', ' ', var_export($arg, TRUE)); - } - -} +definitions[$name])) { + throw new Nette\InvalidStateException("Service '$name' has already been added."); + } + return $this->definitions[$name] = new ServiceDefinition; + } + + + + /** + * Removes the specified service definition. + * @param string + * @return void + */ + public function removeDefinition($name) + { + unset($this->definitions[$name]); + } + + + + /** + * Gets the service definition. + * @param string + * @return ServiceDefinition + */ + public function getDefinition($name) + { + if (!isset($this->definitions[$name])) { + throw new MissingServiceException("Service '$name' not found."); + } + return $this->definitions[$name]; + } + + + + /** + * Gets all service definitions. + * @return array + */ + public function getDefinitions() + { + return $this->definitions; + } + + + + /** + * Does the service definition exist? + * @param string + * @return bool + */ + public function hasDefinition($name) + { + return isset($this->definitions[$name]); + } + + + + /********************* class resolving ****************d*g**/ + + + + /** + * Resolves service name by type. + * @param string class or interface + * @return string service name or NULL + * @throws ServiceCreationException + */ + public function getByType($class) + { + $lower = ltrim(strtolower($class), '\\'); + if (!isset($this->classes[$lower])) { + return; + + } elseif (count($this->classes[$lower]) === 1) { + return $this->classes[$lower][0]; + + } else { + throw new ServiceCreationException("Multiple services of type $class found: " . implode(', ', $this->classes[$lower])); + } + } + + + + /** + * Gets the service objects of the specified tag. + * @param string + * @return array of [service name => tag attributes] + */ + public function findByTag($tag) + { + $found = array(); + foreach ($this->definitions as $name => $def) { + if (isset($def->tags[$tag]) && $def->shared) { + $found[$name] = $def->tags[$tag]; + } + } + return $found; + } + + + + /** + * Creates a list of arguments using autowiring. + * @return array + */ + public function autowireArguments($class, $method, array $arguments) + { + $rc = Nette\Reflection\ClassType::from($class); + if (!$rc->hasMethod($method)) { + if (!Nette\Utils\Validators::isList($arguments)) { + throw new ServiceCreationException("Unable to pass specified arguments to $class::$method()."); + } + return $arguments; + } + + $rm = $rc->getMethod($method); + if ($rm->isAbstract() || !$rm->isPublic()) { + throw new ServiceCreationException("$rm is not callable."); + } + $this->addDependency($rm->getFileName()); + return Helpers::autowireArguments($rm, $arguments, $this); + } + + + + /** + * Generates $dependencies, $classes and expands and normalize class names. + * @return array + */ + public function prepareClassList() + { + // complete class-factory pairs; expand classes + foreach ($this->definitions as $name => $def) { + if ($def->class === self::CREATED_SERVICE || ($def->factory && $def->factory->entity === self::CREATED_SERVICE)) { + $def->class = $name; + $def->internal = TRUE; + if ($def->factory && $def->factory->entity === self::CREATED_SERVICE) { + $def->factory->entity = $def->class; + } + unset($this->definitions[$name]); + $this->definitions['_anonymous_' . str_replace('\\', '_', strtolower(trim($name, '\\')))] = $def; + } + + if ($def->class) { + $def->class = $this->expand($def->class); + if (!$def->factory) { + $def->factory = new Statement($def->class); + } + } elseif (!$def->factory) { + throw new ServiceCreationException("Class and factory are missing in service '$name' definition."); + } + } + + // complete classes + $this->classes = FALSE; + foreach ($this->definitions as $name => $def) { + $this->resolveClass($name); + } + + // build auto-wiring list + $this->classes = array(); + foreach ($this->definitions as $name => $def) { + if (!$def->class) { + continue; + } + if (!class_exists($def->class) && !interface_exists($def->class)) { + throw new Nette\InvalidStateException("Class $def->class has not been found."); + } + $def->class = Nette\Reflection\ClassType::from($def->class)->getName(); + if ($def->autowired) { + foreach (class_parents($def->class) + class_implements($def->class) + array($def->class) as $parent) { + $this->classes[strtolower($parent)][] = $name; + } + } + } + + foreach ($this->classes as $class => $foo) { + $this->addDependency(Nette\Reflection\ClassType::from($class)->getFileName()); + } + } + + + + private function resolveClass($name, $recursive = array()) + { + if (isset($recursive[$name])) { + throw new Nette\InvalidArgumentException('Circular reference detected for services: ' . implode(', ', array_keys($recursive)) . '.'); + } + $recursive[$name] = TRUE; + + $def = $this->definitions[$name]; + $factory = $this->normalizeEntity($this->expand($def->factory->entity)); + + if ($def->class) { + return $def->class; + + } elseif (is_array($factory)) { // method calling + if ($service = $this->getServiceName($factory[0])) { + if (Strings::contains($service, '\\')) { // @\Class + throw new ServiceCreationException("Unable resolve class name for service '$name'."); + } + $factory[0] = $this->resolveClass($service, $recursive); + if (!$factory[0]) { + return; + } + } + $factory = new Nette\Callback($factory); + if (!$factory->isCallable()) { + throw new Nette\InvalidStateException("Factory '$factory' is not callable."); + } + try { + $reflection = $factory->toReflection(); + $def->class = preg_replace('#[|\s].*#', '', $reflection->getAnnotation('return')); + if ($def->class && !class_exists($def->class) && $def->class[0] !== '\\' && $reflection instanceof \ReflectionMethod) { + /**/$def->class = $reflection->getDeclaringClass()->getNamespaceName() . '\\' . $def->class;/**/ + } + } catch (\ReflectionException $e) { + } + + } elseif ($service = $this->getServiceName($factory)) { // alias or factory + if (Strings::contains($service, '\\')) { // @\Class + /*5.2* $service = ltrim($service, '\\');*/ + $def->autowired = FALSE; + return $def->class = $service; + } + if ($this->definitions[$service]->shared) { + $def->autowired = FALSE; + } + return $def->class = $this->resolveClass($service, $recursive); + + } else { + return $def->class = $factory; // class name + } + } + + + + /** + * Adds a file to the list of dependencies. + * @return ContainerBuilder provides a fluent interface + */ + public function addDependency($file) + { + $this->dependencies[$file] = TRUE; + return $this; + } + + + + /** + * Returns the list of dependent files. + * @return array + */ + public function getDependencies() + { + unset($this->dependencies[FALSE]); + return array_keys($this->dependencies); + } + + + + /********************* code generator ****************d*g**/ + + + + /** + * Generates PHP class. + * @return Nette\Utils\PhpGenerator\ClassType + */ + public function generateClass($parentClass = 'Nette\DI\Container') + { + unset($this->definitions[self::THIS_CONTAINER]); + $this->addDefinition(self::THIS_CONTAINER)->setClass($parentClass); + + $this->prepareClassList(); + + $class = new Nette\Utils\PhpGenerator\ClassType('Container'); + $class->addExtend($parentClass); + $class->addMethod('__construct') + ->addBody('parent::__construct(?);', array($this->expand($this->parameters))); + + $classes = $class->addProperty('classes', array()); + foreach ($this->classes as $name => $foo) { + try { + $classes->value[$name] = $this->getByType($name); + } catch (ServiceCreationException $e) { + $classes->value[$name] = new PhpLiteral('FALSE, //' . strstr($e->getMessage(), ':')); + } + } + + $definitions = $this->definitions; + ksort($definitions); + + $meta = $class->addProperty('meta', array()); + foreach ($definitions as $name => $def) { + if ($def->shared) { + foreach ($this->expand($def->tags) as $tag => $value) { + $meta->value[$name][Container::TAGS][$tag] = $value; + } + } + } + + foreach ($definitions as $name => $def) { + try { + $type = $def->class ?: 'object'; + $methodName = Container::getMethodName($name, $def->shared); + if (!PhpHelpers::isIdentifier($methodName)) { + throw new ServiceCreationException('Name contains invalid characters.'); + } + if ($def->shared && !$def->internal && PhpHelpers::isIdentifier($name)) { + $class->addDocument("@property $type \$$name"); + } + $method = $class->addMethod($methodName) + ->addDocument("@return $type") + ->setVisibility($def->shared || $def->internal ? 'protected' : 'public') + ->setBody($name === self::THIS_CONTAINER ? 'return $this;' : $this->generateService($name)); + + foreach ($this->expand($def->parameters) as $k => $v) { + $tmp = explode(' ', is_int($k) ? $v : $k); + $param = is_int($k) ? $method->addParameter(end($tmp)) : $method->addParameter(end($tmp), $v); + if (isset($tmp[1])) { + $param->setTypeHint($tmp[0]); + } + } + } catch (\Exception $e) { + throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); + } + } + + return $class; + } + + + + /** + * Generates body of service method. + * @return string + */ + private function generateService($name) + { + $def = $this->definitions[$name]; + $parameters = $this->parameters; + foreach ($this->expand($def->parameters) as $k => $v) { + $v = explode(' ', is_int($k) ? $v : $k); + $parameters[end($v)] = new PhpLiteral('$' . end($v)); + } + + $code = '$service = ' . $this->formatStatement(Helpers::expand($def->factory, $parameters, TRUE)) . ";\n"; + + $entity = $this->normalizeEntity($def->factory->entity); + if ($def->class && $def->class !== $entity && !$this->getServiceName($entity)) { + $code .= PhpHelpers::formatArgs("if (!\$service instanceof $def->class) {\n" + . "\tthrow new Nette\\UnexpectedValueException(?);\n}\n", + array("Unable to create service '$name', value returned by factory is not $def->class type.") + ); + } + + foreach ((array) $def->setup as $setup) { + $setup = Helpers::expand($setup, $parameters, TRUE); + if (is_string($setup->entity) && strpbrk($setup->entity, ':@?') === FALSE) { // auto-prepend @self + $setup->entity = array("@$name", $setup->entity); + } + $code .= $this->formatStatement($setup, $name) . ";\n"; + } + + return $code .= 'return $service;'; + } + + + + /** + * Formats PHP code for class instantiating, function calling or property setting in PHP. + * @return string + * @internal + */ + public function formatStatement(Statement $statement, $self = NULL) + { + $entity = $this->normalizeEntity($statement->entity); + $arguments = $statement->arguments; + + if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal + return $this->formatPhp($entity, $arguments, $self); + + } elseif ($service = $this->getServiceName($entity)) { // factory calling or service retrieving + if ($this->definitions[$service]->shared) { + if ($arguments) { + throw new ServiceCreationException("Unable to call service '$entity'."); + } + return $this->formatPhp('$this->getService(?)', array($service)); + } + $params = array(); + foreach ($this->definitions[$service]->parameters as $k => $v) { + $params[] = preg_replace('#\w+$#', '\$$0', (is_int($k) ? $v : $k)) . (is_int($k) ? '' : ' = ' . PhpHelpers::dump($v)); + } + $rm = new Nette\Reflection\GlobalFunction(create_function(implode(', ', $params), '')); + $arguments = Helpers::autowireArguments($rm, $arguments, $this); + return $this->formatPhp('$this->?(?*)', array(Container::getMethodName($service, FALSE), $arguments), $self); + + } elseif ($entity === 'not') { // operator + return $this->formatPhp('!?', array($arguments[0])); + + } elseif (is_string($entity)) { // class name + if ($constructor = Nette\Reflection\ClassType::from($entity)->getConstructor()) { + $this->addDependency($constructor->getFileName()); + $arguments = Helpers::autowireArguments($constructor, $arguments, $this); + } elseif ($arguments) { + throw new ServiceCreationException("Unable to pass arguments, class $entity has no constructor."); + } + return $this->formatPhp("new $entity" . ($arguments ? '(?*)' : ''), array($arguments), $self); + + } elseif (!Validators::isList($entity) || count($entity) !== 2) { + throw new Nette\InvalidStateException("Expected class, method or property, " . PhpHelpers::dump($entity) . " given."); + + } elseif ($entity[0] === '') { // globalFunc + return $this->formatPhp("$entity[1](?*)", array($arguments), $self); + + } elseif (Strings::contains($entity[1], '$')) { // property setter + Validators::assert($arguments, 'list:1', "setup arguments for '" . Nette\Callback::create($entity) . "'"); + if ($this->getServiceName($entity[0], $self)) { + return $this->formatPhp('?->? = ?', array($entity[0], substr($entity[1], 1), $arguments[0]), $self); + } else { + return $this->formatPhp($entity[0] . '::$? = ?', array(substr($entity[1], 1), $arguments[0]), $self); + } + + } elseif ($service = $this->getServiceName($entity[0], $self)) { // service method + if ($this->definitions[$service]->class) { + $arguments = $this->autowireArguments($this->definitions[$service]->class, $entity[1], $arguments); + } + return $this->formatPhp('?->?(?*)', array($entity[0], $entity[1], $arguments), $self); + + } else { // static method + $arguments = $this->autowireArguments($entity[0], $entity[1], $arguments); + return $this->formatPhp("$entity[0]::$entity[1](?*)", array($arguments), $self); + } + } + + + + /** + * Formats PHP statement. + * @return string + */ + public function formatPhp($statement, $args, $self = NULL) + { + $that = $this; + array_walk_recursive($args, function(&$val) use ($self, $that) { + list($val) = $that->normalizeEntity(array($val)); + + if ($val instanceof Statement) { + $val = new PhpLiteral($that->formatStatement($val, $self)); + + } elseif ($val === '@' . ContainerBuilder::THIS_CONTAINER) { + $val = new PhpLiteral('$this'); + + } elseif ($service = $that->getServiceName($val, $self)) { + $val = $service === $self ? '$service' : $that->formatStatement(new Statement($val)); + $val = new PhpLiteral($val); + } + }); + return PhpHelpers::formatArgs($statement, $args); + } + + + + /** + * Expands %placeholders% in strings (recursive). + * @param mixed + * @return mixed + */ + public function expand($value) + { + return Helpers::expand($value, $this->parameters, TRUE); + } + + + + /** @internal */ + public function normalizeEntity($entity) + { + if (is_string($entity) && Strings::contains($entity, '::') && !Strings::contains($entity, '?')) { // Class::method -> [Class, method] + $entity = explode('::', $entity); + } + + if (is_array($entity) && $entity[0] instanceof ServiceDefinition) { // [ServiceDefinition, ...] -> [@serviceName, ...] + $tmp = array_keys($this->definitions, $entity[0], TRUE); + $entity[0] = "@$tmp[0]"; + + } elseif ($entity instanceof ServiceDefinition) { // ServiceDefinition -> @serviceName + $tmp = array_keys($this->definitions, $entity, TRUE); + $entity = "@$tmp[0]"; + + } elseif (is_array($entity) && $entity[0] === $this) { // [$this, ...] -> [@container, ...] + $entity[0] = '@' . ContainerBuilder::THIS_CONTAINER; + } + return $entity; // Class, @service, [Class, member], [@service, member], [, globalFunc] + } + + + + /** + * Converts @service or @\Class -> service name and checks its existence. + * @param mixed + * @return string of FALSE, if argument is not service name + */ + public function getServiceName($arg, $self = NULL) + { + if (!is_string($arg) || !preg_match('#^@[\w\\\\.].+$#', $arg)) { + return FALSE; + } + $service = substr($arg, 1); + if ($service === self::CREATED_SERVICE) { + $service = $self; + } + if (Strings::contains($service, '\\')) { + if ($this->classes === FALSE) { // may be disabled by prepareClassList + return $service; + } + $res = $this->getByType($service); + if (!$res) { + throw new ServiceCreationException("Reference to missing service of type $service."); + } + return $res; + } + if (!isset($this->definitions[$service])) { + throw new ServiceCreationException("Reference to missing service '$service'."); + } + return $service; + } + +} diff --git a/libs/Nette/DI/Diagnostics/ContainerPanel.php b/libs/Nette/DI/Diagnostics/ContainerPanel.php new file mode 100644 index 0000000..8366d44 --- /dev/null +++ b/libs/Nette/DI/Diagnostics/ContainerPanel.php @@ -0,0 +1,93 @@ +container = $container; + } + + + + /** + * Renders tab. + * @return string + */ + public function getTab() + { + ob_start(); + require __DIR__ . '/templates/ContainerPanel.tab.phtml'; + return ob_get_clean(); + } + + + + /** + * Renders panel. + * @return string + */ + public function getPanel() + { + $services = $this->getContainerProperty('factories'); + $factories = array(); + foreach (Nette\Reflection\ClassType::from($this->container)->getMethods() as $method) { + if (preg_match('#^create(Service)?(.+)$#', $method->getName(), $m)) { + $name = str_replace('__', '.', strtolower(substr($m[2], 0, 1)) . substr($m[2], 1)); + if ($m[1]) { + $services[$name] = $method->getAnnotation('return'); + } elseif ($method->isPublic()) { + $a = strrpos(".$name", '.'); + $factories[substr($name, 0, $a) . 'create' . ucfirst(substr($name, $a))] = $method->getAnnotation('return'); + } + } + } + ksort($services); + ksort($factories); + $container = $this->container; + $registry = $this->getContainerProperty('registry'); + + ob_start(); + require __DIR__ . '/templates/ContainerPanel.panel.phtml'; + return ob_get_clean(); + } + + + + private function getContainerProperty($name) + { + $prop = Nette\Reflection\ClassType::from('Nette\DI\Container')->getProperty($name); + $prop->setAccessible(TRUE); + return $prop->getValue($this->container); + } + +} diff --git a/libs/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml b/libs/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml new file mode 100644 index 0000000..c2dd2b9 --- /dev/null +++ b/libs/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml @@ -0,0 +1,99 @@ + + + +
    +

    container) ?>

    + +
    +

    Parameters

    + +
    + container->parameters); ?> +
    + +

    Services

    + + + + + + + + + + + + $class): ?> + classes, $name); ?> + + + + + + + + +
    NameAutowiredServiceMeta
    ', $name)) ?>" class=""> + + + + + + + + meta[$name])) { echo Helpers::clickableDump($container->meta[$name], TRUE); } ?>
    + +

    Factories

    + + + + + + + + + + $class): ?> + + + + + + +
    MethodReturns
    ', $name)) ?>()
    +
    +
    diff --git a/libs/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml b/libs/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml new file mode 100644 index 0000000..fc4e380 --- /dev/null +++ b/libs/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml @@ -0,0 +1,9 @@ + +  diff --git a/libs/Nette/DI/Helpers.php b/libs/Nette/DI/Helpers.php new file mode 100644 index 0000000..b53af78 --- /dev/null +++ b/libs/Nette/DI/Helpers.php @@ -0,0 +1,160 @@ + $val) { + $res[$key] = self::expand($val, $params, $recursive); + } + return $res; + + } elseif ($var instanceof Statement) { + return new Statement(self::expand($var->entity, $params, $recursive), self::expand($var->arguments, $params, $recursive)); + + } elseif (!is_string($var)) { + return $var; + } + + $parts = preg_split('#%([\w.-]*)%#i', $var, -1, PREG_SPLIT_DELIM_CAPTURE); + $res = ''; + foreach ($parts as $n => $part) { + if ($n % 2 === 0) { + $res .= $part; + + } elseif ($part === '') { + $res .= '%'; + + } elseif (isset($recursive[$part])) { + throw new Nette\InvalidArgumentException('Circular reference detected for variables: ' . implode(', ', array_keys($recursive)) . '.'); + + } else { + $val = Nette\Utils\Arrays::get($params, explode('.', $part)); + if ($recursive) { + $val = self::expand($val, $params, (is_array($recursive) ? $recursive : array()) + array($part => 1)); + } + if (strlen($part) + 2 === strlen($var)) { + return $val; + } + if (!is_scalar($val)) { + throw new Nette\InvalidArgumentException("Unable to concatenate non-scalar parameter '$part' into '$var'."); + } + $res .= $val; + } + } + return $res; + } + + + + /** + * Expand counterpart. + * @param mixed + * @return mixed + */ + public static function escape($value) + { + if (is_array($value)) { + array_walk_recursive($value, function(&$val) { + $val = is_string($val) ? str_replace('%', '%%', $val) : $val; + }); + } elseif (is_string($value)) { + $value = str_replace('%', '%%', $value); + } + return $value; + } + + + + /** + * Generates list of arguments using autowiring. + * @param Nette\Reflection\GlobalFunction|Nette\Reflection\Method + * @return array + */ + public static function autowireArguments(\ReflectionFunctionAbstract $method, array $arguments, $container) + { + $optCount = 0; + $num = -1; + $res = array(); + + foreach ($method->getParameters() as $num => $parameter) { + if (array_key_exists($num, $arguments)) { + $res[$num] = $arguments[$num]; + unset($arguments[$num]); + $optCount = 0; + + } elseif (array_key_exists($parameter->getName(), $arguments)) { + $res[$num] = $arguments[$parameter->getName()]; + unset($arguments[$parameter->getName()]); + $optCount = 0; + + } elseif ($class = $parameter->getClassName()) { // has object type hint + $res[$num] = $container->getByType($class, FALSE); + if ($res[$num] === NULL) { + if ($parameter->allowsNull()) { + $optCount++; + } else { + throw new ServiceCreationException("No service of type {$class} found. Make sure the type hint in $method is written correctly and service of this type is registered."); + } + } else { + if ($container instanceof ContainerBuilder) { + $res[$num] = '@' . $res[$num]; + } + $optCount = 0; + } + + } elseif ($parameter->isOptional()) { + // PDO::__construct has optional parameter without default value (and isArray() and allowsNull() returns FALSE) + $res[$num] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL; + $optCount++; + + } else { + throw new ServiceCreationException("Parameter $parameter has no type hint, so its value must be specified."); + } + } + + // extra parameters + while (array_key_exists(++$num, $arguments)) { + $res[$num] = $arguments[$num]; + unset($arguments[$num]); + $optCount = 0; + } + if ($arguments) { + throw new ServiceCreationException("Unable to pass specified arguments to $method."); + } + + return $optCount ? array_slice($res, 0, -$optCount) : $res; + } + +} diff --git a/libs/Nette/DI/IContainer.php b/libs/Nette/DI/IContainer.php index 16b9a35..ddb39d7 100644 --- a/libs/Nette/DI/IContainer.php +++ b/libs/Nette/DI/IContainer.php @@ -1,54 +1,52 @@ -container = $container; + $this->namespace = $namespace . '.'; + $this->parameters = & $container->parameters[$namespace]; + } + + + + /** + * @return object + */ + public function __call($name, $args) + { + if (substr($name, 0, 6) === 'create') { + return call_user_func_array(array( + $this->container, + Container::getMethodName($this->namespace . substr($name, 6), FALSE) + ), $args); + } + throw new Nette\NotSupportedException; + } + + + + /** + * @return object + */ + public function &__get($name) + { + $service = $this->container->getService($this->namespace . $name); + return $service; + } + + + + /** + * @return void + */ + public function __set($name, $service) + { + throw new Nette\NotSupportedException; + } + + + + /** + * @return bool + */ + public function __isset($name) + { + return $this->container->hasService($this->namespace . $name); + } + + + + /** + * @return void + */ + public function __unset($name) + { + throw new Nette\NotSupportedException; + } + +} diff --git a/libs/Nette/DI/ServiceBuilder.php b/libs/Nette/DI/ServiceBuilder.php deleted file mode 100644 index cc807b1..0000000 --- a/libs/Nette/DI/ServiceBuilder.php +++ /dev/null @@ -1,52 +0,0 @@ -class = $class; - } - - - - public function getClass() - { - return $this->class; - } - - - - public function createService(Nette\DI\IContainer $container) - { - if (!class_exists($this->class)) { - throw new Nette\InvalidStateException("Cannot instantiate service, class '$this->class' not found."); - } - return new $this->class; - } - -} diff --git a/libs/Nette/DI/ServiceDefinition.php b/libs/Nette/DI/ServiceDefinition.php new file mode 100644 index 0000000..eede295 --- /dev/null +++ b/libs/Nette/DI/ServiceDefinition.php @@ -0,0 +1,134 @@ +class = $class; + if ($args) { + $this->setFactory($class, $args); + } + return $this; + } + + + + public function setFactory($factory, array $args = array()) + { + $this->factory = new Statement($factory, $args); + return $this; + } + + + + public function setArguments(array $args = array()) + { + if ($this->factory) { + $this->factory->arguments = $args; + } else { + $this->setClass($this->class, $args); + } + return $this; + } + + + + public function addSetup($target, $args = NULL) + { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + $this->setup[] = new Statement($target, $args); + return $this; + } + + + + public function setParameters(array $params) + { + $this->shared = $this->autowired = FALSE; + $this->parameters = $params; + return $this; + } + + + + public function addTag($tag, $attrs = TRUE) + { + $this->tags[$tag] = $attrs; + return $this; + } + + + + public function setAutowired($on) + { + $this->autowired = $on; + return $this; + } + + + + public function setShared($on) + { + $this->shared = (bool) $on; + $this->autowired = $this->shared ? $this->autowired : FALSE; + return $this; + } + + + + public function setInternal($on) + { + $this->internal = (bool) $on; + return $this; + } + +} diff --git a/libs/Nette/DI/Statement.php b/libs/Nette/DI/Statement.php new file mode 100644 index 0000000..c114f9e --- /dev/null +++ b/libs/Nette/DI/Statement.php @@ -0,0 +1,39 @@ +entity = $entity; + $this->arguments = $arguments; + } + +} diff --git a/libs/Nette/DI/exceptions.php b/libs/Nette/DI/exceptions.php index 9934e41..bc19f91 100644 --- a/libs/Nette/DI/exceptions.php +++ b/libs/Nette/DI/exceptions.php @@ -1,32 +1,32 @@ -setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Nette\Database\Statement', array($this))); - - $class = 'Nette\Database\Drivers\\' . $this->getAttribute(PDO::ATTR_DRIVER_NAME) . 'Driver'; - if (class_exists($class)) { - $this->driver = new $class($this, (array) $options); - } - - $this->preprocessor = new SqlPreprocessor($this); - - $this->databaseReflection = new Reflection\DatabaseReflection; // TODO - - Diagnostics\ConnectionPanel::initialize($this); - } - - - - /** @return ISupplementalDriver */ - public function getSupplementalDriver() - { - return $this->driver; - } - - - - /** - * Generates and executes SQL query. - * @param string statement - * @param mixed [parameters, ...] - * @return Statement - */ - public function query($statement) - { - $args = func_get_args(); - return $this->queryArgs(array_shift($args), $args); - } - - - - /** - * Generates and executes SQL query. - * @param string statement - * @param mixed [parameters, ...] - * @return int number of affected rows - */ - public function exec($statement) - { - $args = func_get_args(); - return $this->queryArgs(array_shift($args), $args)->rowCount(); - } - - - - /** - * @param string statement - * @param array - * @return Statement - */ - public function queryArgs($statement, $params) - { - foreach ($params as $value) { - if (is_array($value) || is_object($value)) { - $need = TRUE; break; - } - } - if (isset($need) || strpos($statement, ':') !== FALSE && $this->preprocessor !== NULL) { - list($statement, $params) = $this->preprocessor->process($statement, $params); - } - - return $this->prepare($statement)->execute($params); - } - - - - /********************* shortcuts ****************d*g**/ - - - - /** - * Shortcut for query()->fetch() - * @param string statement - * @param mixed [parameters, ...] - * @return Row - */ - public function fetch($args) - { - $args = func_get_args(); - return $this->queryArgs(array_shift($args), $args)->fetch(); - } - - - - /** - * Shortcut for query()->fetchColumn() - * @param string statement - * @param mixed [parameters, ...] - * @return mixed - */ - public function fetchColumn($args) - { - $args = func_get_args(); - return $this->queryArgs(array_shift($args), $args)->fetchColumn(); - } - - - - /** - * Shortcut for query()->fetchPairs() - * @param string statement - * @param mixed [parameters, ...] - * @return array - */ - public function fetchPairs($args) - { - $args = func_get_args(); - return $this->queryArgs(array_shift($args), $args)->fetchPairs(); - } - - - - /** - * Shortcut for query()->fetchAll() - * @param string statement - * @param mixed [parameters, ...] - * @return array - */ - public function fetchAll($args) - { - $args = func_get_args(); - return $this->queryArgs(array_shift($args), $args)->fetchAll(); - } - - - - /********************* selector ****************d*g**/ - - - - /** - * Creates selector for table. - * @param string - * @return Nette\Database\Table\Selection - */ - public function table($table) - { - return new Table\Selection($table, $this); - } - - - - /********************* misc ****************d*g**/ - - - - /** - * Import SQL dump from file - extreme fast. - * @param string filename - * @return int count of commands - */ - public function loadFile($file) - { - @set_time_limit(0); // intentionally @ - - $handle = @fopen($file, 'r'); // intentionally @ - if (!$handle) { - throw new Nette\FileNotFoundException("Cannot open file '$file'."); - } - - $count = 0; - $sql = ''; - while (!feof($handle)) { - $s = fgets($handle); - $sql .= $s; - if (substr(rtrim($s), -1) === ';') { - parent::exec($sql); // native query without logging - $sql = ''; - $count++; - } - } - fclose($handle); - return $count; - } - - - - /** - * Returns syntax highlighted SQL command. - * @param string - * @return string - */ - public static function highlightSql($sql) - { - static $keywords1 = 'SELECT|UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; - static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|TRUE|FALSE'; - - // insert new lines - $sql = " $sql "; - $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); - - // reduce spaces - $sql = preg_replace('#[ \t]{2,}#', " ", $sql); - - $sql = wordwrap($sql, 100); - $sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql); - - // syntax highlight - $sql = htmlSpecialChars($sql); - $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", function($matches) { - if (!empty($matches[1])) // comment - return '' . $matches[1] . ''; - - if (!empty($matches[2])) // error - return '' . $matches[2] . ''; - - if (!empty($matches[3])) // most important keywords - return '' . $matches[3] . ''; - - if (!empty($matches[4])) // other keywords - return '' . $matches[4] . ''; - }, $sql); - - return '
    ' . trim($sql) . "
    \n"; - } - - - - /********************* Nette\Object behaviour ****************d*g**/ - - - - /** - * @return Nette\Reflection\ClassType - */ - public static function getReflection() - { - return new Nette\Reflection\ClassType(get_called_class()); - } - - - - public function __call($name, $args) - { - return ObjectMixin::call($this, $name, $args); - } - - - - public function &__get($name) - { - return ObjectMixin::get($this, $name); - } - - - - public function __set($name, $value) - { - return ObjectMixin::set($this, $name, $value); - } - - - - public function __isset($name) - { - return ObjectMixin::has($this, $name); - } - - - - public function __unset($name) - { - ObjectMixin::remove($this, $name); - } - -} +dsn = $dsn, $username, $password, $options); + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Nette\Database\Statement', array($this))); + + $driverClass = $driverClass ?: 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver'; + $this->driver = new $driverClass($this, (array) $options); + $this->preprocessor = new SqlPreprocessor($this); + } + + + + public function getDsn() + { + return $this->dsn; + } + + + + /** @return ISupplementalDriver */ + public function getSupplementalDriver() + { + return $this->driver; + } + + + + /** + * Sets database reflection. + * @return Connection provides a fluent interface + */ + public function setDatabaseReflection(IReflection $databaseReflection) + { + $databaseReflection->setConnection($this); + $this->databaseReflection = $databaseReflection; + return $this; + } + + + + /** @return IReflection */ + public function getDatabaseReflection() + { + if (!$this->databaseReflection) { + $this->setDatabaseReflection(new Reflection\ConventionalReflection); + } + return $this->databaseReflection; + } + + + + /** + * Sets cache storage engine. + * @return Connection provides a fluent interface + */ + public function setCacheStorage(Nette\Caching\IStorage $storage = NULL) + { + $this->cache = $storage ? new Nette\Caching\Cache($storage, 'Nette.Database.' . md5($this->dsn)) : NULL; + return $this; + } + + + + public function getCache() + { + return $this->cache; + } + + + + /** + * Generates and executes SQL query. + * @param string statement + * @param mixed [parameters, ...] + * @return Statement + */ + public function query($statement) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args); + } + + + + /** + * Generates and executes SQL query. + * @param string statement + * @param mixed [parameters, ...] + * @return int number of affected rows + */ + public function exec($statement) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->rowCount(); + } + + + + /** + * @param string statement + * @param array + * @return Statement + */ + public function queryArgs($statement, $params) + { + foreach ($params as $value) { + if (is_array($value) || is_object($value)) { + $need = TRUE; break; + } + } + if (isset($need) && $this->preprocessor !== NULL) { + list($statement, $params) = $this->preprocessor->process($statement, $params); + } + + return $this->prepare($statement)->execute($params); + } + + + + /********************* shortcuts ****************d*g**/ + + + + /** + * Shortcut for query()->fetch() + * @param string statement + * @param mixed [parameters, ...] + * @return Row + */ + public function fetch($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetch(); + } + + + + /** + * Shortcut for query()->fetchColumn() + * @param string statement + * @param mixed [parameters, ...] + * @return mixed + */ + public function fetchColumn($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetchColumn(); + } + + + + /** + * Shortcut for query()->fetchPairs() + * @param string statement + * @param mixed [parameters, ...] + * @return array + */ + public function fetchPairs($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetchPairs(); + } + + + + /** + * Shortcut for query()->fetchAll() + * @param string statement + * @param mixed [parameters, ...] + * @return array + */ + public function fetchAll($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetchAll(); + } + + + + /********************* selector ****************d*g**/ + + + + /** + * Creates selector for table. + * @param string + * @return Nette\Database\Table\Selection + */ + public function table($table) + { + return new Table\Selection($table, $this); + } + + + + /********************* Nette\Object behaviour ****************d*g**/ + + + + /** + * @return Nette\Reflection\ClassType + */ + public /**/static/**/ function getReflection() + { + return new Nette\Reflection\ClassType(/*5.2*$this*//**/get_called_class()/**/); + } + + + + public function __call($name, $args) + { + return ObjectMixin::call($this, $name, $args); + } + + + + public function &__get($name) + { + return ObjectMixin::get($this, $name); + } + + + + public function __set($name, $value) + { + return ObjectMixin::set($this, $name, $value); + } + + + + public function __isset($name) + { + return ObjectMixin::has($this, $name); + } + + + + public function __unset($name) + { + ObjectMixin::remove($this, $name); + } + +} diff --git a/libs/Nette/Database/Diagnostics/ConnectionPanel.php b/libs/Nette/Database/Diagnostics/ConnectionPanel.php index 6979b5b..316741e 100644 --- a/libs/Nette/Database/Diagnostics/ConnectionPanel.php +++ b/libs/Nette/Database/Diagnostics/ConnectionPanel.php @@ -1,162 +1,159 @@ -onQuery[] = callback($panel, 'logQuery'); - Debugger::$bar->addPanel($panel); - Debugger::$blueScreen->addPanel(callback($panel, 'renderException'), __CLASS__); - } - } - - - - public function logQuery(Nette\Database\Statement $result, array $params = NULL) - { - if ($this->disabled) { - return; - } - $source = NULL; - foreach (debug_backtrace(FALSE) as $row) { - if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], NETTE_DIR . DIRECTORY_SEPARATOR) !== 0) { - $source = array($row['file'], (int) $row['line']); - break; - } - } - $this->totalTime += $result->time; - $this->queries[] = array($result->queryString, $params, $result->time, $result->rowCount(), $result->getConnection(), $source); - } - - - - public function renderException($e) - { - if ($e instanceof \PDOException && isset($e->queryString)) { - return array( - 'tab' => 'SQL', - 'panel' => Connection::highlightSql($e->queryString), - ); - } - } - - - - public function getTab() - { - return '' - . '' - . count($this->queries) . ' queries' - . ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '') - . ''; - } - - - - public function getPanel() - { - $this->disabled = TRUE; - $s = ''; - $h = 'htmlSpecialChars'; - foreach ($this->queries as $i => $query) { - list($sql, $params, $time, $rows, $connection, $source) = $query; - - $explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS() - if ($this->explain && preg_match('#\s*SELECT\s#iA', $sql)) { - try { - $explain = $connection->queryArgs('EXPLAIN ' . $sql, $params)->fetchAll(); - } catch (\PDOException $e) {} - } - - $s .= '' . sprintf('%0.3f', $time * 1000); - if ($explain) { - static $counter; - $counter++; - $s .= "
    explain ►"; - } - - $s .= '' . Connection::highlightSql(Nette\Utils\Strings::truncate($sql, self::$maxLength)); - if ($explain) { - $s .= ""; - foreach ($explain[0] as $col => $foo) { - $s .= ""; - } - $s .= ""; - foreach ($explain as $row) { - $s .= ""; - foreach ($row as $col) { - $s .= ""; - } - $s .= ""; - } - $s .= "
    {$h($col)}
    {$h($col)}
    "; - } - if ($source) { - $s .= Nette\Diagnostics\Helpers::editorLink($source[0], $source[1])->class('nette-DbConnectionPanel-source'); - } - - $s .= ''; - foreach ($params as $param) { - $s .= Debugger::dump($param, TRUE); - } - - $s .= '' . $rows . ''; - } - - return empty($this->queries) ? '' : - ' -

    Queries: ' . count($this->queries) . ($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') . '

    -
    - - ' . $s . ' -
    Time msSQL StatementParamsRows
    -
    '; - } - -} +disabled) { + return; + } + $source = NULL; + foreach (/*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE) as $row) { + if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], NETTE_DIR . DIRECTORY_SEPARATOR) !== 0) { + if (isset($row['function']) && strpos($row['function'], 'call_user_func') === 0) continue; + if (isset($row['class']) && is_subclass_of($row['class'], '\\Nette\\Database\\Connection')) continue; + $source = array($row['file'], (int) $row['line']); + break; + } + } + $this->totalTime += $result->getTime(); + $this->queries[] = array($result->queryString, $params, $result->getTime(), $result->rowCount(), $result->getConnection(), $source); + } + + + + public static function renderException($e) + { + if (!$e instanceof \PDOException) { + return; + } + if (isset($e->queryString)) { + $sql = $e->queryString; + + } elseif ($item = Nette\Diagnostics\Helpers::findTrace($e->getTrace(), 'PDO::prepare')) { + $sql = $item['args'][0]; + } + return isset($sql) ? array( + 'tab' => 'SQL', + 'panel' => Helpers::dumpSql($sql), + ) : NULL; + } + + + + public function getTab() + { + return '' + . '' + . count($this->queries) . ' queries' + . ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '') + . ''; + } + + + + public function getPanel() + { + $this->disabled = TRUE; + $s = ''; + $h = 'htmlSpecialChars'; + foreach ($this->queries as $i => $query) { + list($sql, $params, $time, $rows, $connection, $source) = $query; + + $explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS() + if ($this->explain && preg_match('#\s*\(?\s*SELECT\s#iA', $sql)) { + try { + $cmd = is_string($this->explain) ? $this->explain : 'EXPLAIN'; + $explain = $connection->queryArgs("$cmd $sql", $params)->fetchAll(); + } catch (\PDOException $e) {} + } + + $s .= '' . sprintf('%0.3f', $time * 1000); + if ($explain) { + static $counter; + $counter++; + $s .= "
    explain ►"; + } + + $s .= '' . Helpers::dumpSql(self::$maxLength ? Nette\Utils\Strings::truncate($sql, self::$maxLength) : $sql); + if ($explain) { + $s .= ""; + foreach ($explain[0] as $col => $foo) { + $s .= ""; + } + $s .= ""; + foreach ($explain as $row) { + $s .= ""; + foreach ($row as $col) { + $s .= ""; + } + $s .= ""; + } + $s .= "
    {$h($col)}
    {$h($col)}
    "; + } + if ($source) { + $s .= Nette\Diagnostics\Helpers::editorLink($source[0], $source[1])->class('nette-DbConnectionPanel-source'); + } + + $s .= ''; + foreach ($params as $param) { + $s .= Debugger::dump($param, TRUE); + } + + $s .= '' . $rows . ''; + } + + return empty($this->queries) ? '' : + ' +

    Queries: ' . count($this->queries) . ($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') . '

    +
    + + ' . $s . ' +
    Time msSQL StatementParamsRows
    +
    '; + } + +} diff --git a/libs/Nette/Database/Drivers/MsSqlDriver.php b/libs/Nette/Database/Drivers/MsSqlDriver.php index 03967e3..fad898d 100644 --- a/libs/Nette/Database/Drivers/MsSqlDriver.php +++ b/libs/Nette/Database/Drivers/MsSqlDriver.php @@ -1,101 +1,162 @@ - TRUE); - - /** @var Nette\Database\Connection */ - private $connection; - - - - public function __construct(Nette\Database\Connection $connection, array $options) - { - $this->connection = $connection; - } - - - - /********************* SQL ****************d*g**/ - - - - /** - * Delimites identifier for use in a SQL statement. - */ - public function delimite($name) - { - // @see http://msdn.microsoft.com/en-us/library/ms176027.aspx - return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']'; - } - - - - /** - * Formats date-time for use in a SQL statement. - */ - public function formatDateTime(\DateTime $value) - { - return $value->format("'Y-m-d H:i:s'"); - } - - - - /** - * Encodes string for use in a LIKE statement. - */ - public function formatLike($value, $pos) - { - $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]')); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(&$sql, $limit, $offset) - { - // offset support is missing - if ($limit >= 0) { - $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; - } - - if ($offset) { - throw new Nette\NotImplementedException('Offset is not implemented.'); - } - } - - - - /** - * Normalizes result row. - */ - public function normalizeRow($row, $statement) - { - return $row; - } - -} +connection = $connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://msdn.microsoft.com/en-us/library/ms176027.aspx + return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']'; + } + + + + /** + * Formats boolean for use in a SQL statement. + */ + public function formatBool($value) + { + return $value ? '1' : '0'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("'Y-m-d H:i:s'"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]')); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + // offset support is missing + if ($limit >= 0) { + $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; + } + + if ($offset) { + throw new Nette\NotImplementedException('Offset is not implemented.'); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::SUPPORT_COLUMNS_META; + } + +} diff --git a/libs/Nette/Database/Drivers/MySqlDriver.php b/libs/Nette/Database/Drivers/MySqlDriver.php index 0170546..5441d59 100644 --- a/libs/Nette/Database/Drivers/MySqlDriver.php +++ b/libs/Nette/Database/Drivers/MySqlDriver.php @@ -1,111 +1,233 @@ - TRUE); - - /** @var Nette\Database\Connection */ - private $connection; - - - - /** - * Driver options: - * - charset => character encoding to set (default is utf8) - * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html - */ - public function __construct(Nette\Database\Connection $connection, array $options) - { - $this->connection = $connection; - $charset = isset($options['charset']) ? $options['charset'] : 'utf8'; - if ($charset) { - $connection->exec("SET NAMES '$charset'"); - } - if (isset($options['sqlmode'])) { - $connection->exec("SET sql_mode='$options[sqlmode]'"); - } - $connection->exec("SET time_zone='" . date('P') . "'"); - } - - - - /********************* SQL ****************d*g**/ - - - - /** - * Delimites identifier for use in a SQL statement. - */ - public function delimite($name) - { - // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html - return '`' . str_replace('`', '``', $name) . '`'; - } - - - - /** - * Formats date-time for use in a SQL statement. - */ - public function formatDateTime(\DateTime $value) - { - return $value->format("'Y-m-d H:i:s'"); - } - - - - /** - * Encodes string for use in a LIKE statement. - */ - public function formatLike($value, $pos) - { - $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit >= 0 || $offset > 0) { - // see http://dev.mysql.com/doc/refman/5.0/en/select.html - $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) - . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); - } - } - - - - /** - * Normalizes result row. - */ - public function normalizeRow($row, $statement) - { - return $row; - } - -} + character encoding to set (default is utf8) + * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html + */ + public function __construct(Nette\Database\Connection $connection, array $options) + { + $this->connection = $connection; + $charset = isset($options['charset']) ? $options['charset'] : 'utf8'; + if ($charset) { + $connection->exec("SET NAMES '$charset'"); + } + if (isset($options['sqlmode'])) { + $connection->exec("SET sql_mode='$options[sqlmode]'"); + } + $connection->exec("SET time_zone='" . date('P') . "'"); + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html + return '`' . str_replace('`', '``', $name) . '`'; + } + + + + /** + * Formats boolean for use in a SQL statement. + */ + public function formatBool($value) + { + return $value ? '1' : '0'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("'Y-m-d H:i:s'"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0 || $offset > 0) { + // see http://dev.mysql.com/doc/refman/5.0/en/select.html + $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) + . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + /*$this->connection->query(" + SELECT TABLE_NAME as name, TABLE_TYPE = 'VIEW' as view + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = DATABASE() + ");*/ + $tables = array(); + foreach ($this->connection->query('SHOW FULL TABLES') as $row) { + $tables[] = array( + 'name' => $row[0], + 'view' => isset($row[1]) && $row[1] === 'VIEW', + ); + } + return $tables; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + /*$this->connection->query(" + SELECT * + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = {$this->connection->quote($table)} AND TABLE_SCHEMA = DATABASE() + ");*/ + $columns = array(); + foreach ($this->connection->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table)) as $row) { + $type = explode('(', $row['Type']); + $columns[] = array( + 'name' => $row['Field'], + 'table' => $table, + 'nativetype' => strtoupper($type[0]), + 'size' => isset($type[1]) ? (int) $type[1] : NULL, + 'unsigned' => (bool) strstr($row['Type'], 'unsigned'), + 'nullable' => $row['Null'] === 'YES', + 'default' => $row['Default'], + 'autoincrement' => $row['Extra'] === 'auto_increment', + 'primary' => $row['Key'] === 'PRI', + 'vendor' => (array) $row, + ); + } + return $columns; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + /*$this->connection->query(" + SELECT * + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE TABLE_NAME = {$this->connection->quote($table)} AND TABLE_SCHEMA = DATABASE() + AND REFERENCED_COLUMN_NAME IS NULL + ");*/ + $indexes = array(); + foreach ($this->connection->query('SHOW INDEX FROM ' . $this->delimite($table)) as $row) { + $indexes[$row['Key_name']]['name'] = $row['Key_name']; + $indexes[$row['Key_name']]['unique'] = !$row['Non_unique']; + $indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY'; + $indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name']; + } + return array_values($indexes); + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + $keys = array(); + $query = 'SELECT CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE ' + . 'WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = ' . $this->connection->quote($table); + + foreach ($this->connection->query($query) as $id => $row) { + $keys[$id]['name'] = $row['CONSTRAINT_NAME']; // foreign key name + $keys[$id]['local'] = $row['COLUMN_NAME']; // local columns + $keys[$id]['table'] = $row['REFERENCED_TABLE_NAME']; // referenced table + $keys[$id]['foreign'] = $row['REFERENCED_COLUMN_NAME']; // referenced columns + } + + return array_values($keys); + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::SUPPORT_COLUMNS_META || $item == self::SUPPORT_SELECT_UNGROUPED_COLUMNS; + } + +} diff --git a/libs/Nette/Database/Drivers/OciDriver.php b/libs/Nette/Database/Drivers/OciDriver.php index 57ddcdb..81db616 100644 --- a/libs/Nette/Database/Drivers/OciDriver.php +++ b/libs/Nette/Database/Drivers/OciDriver.php @@ -1,105 +1,175 @@ - TRUE); - - /** @var Nette\Database\Connection */ - private $connection; - - /** @var string Datetime format */ - private $fmtDateTime; - - - - public function __construct(Nette\Database\Connection $connection, array $options) - { - $this->connection = $connection; - $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U'; - } - - - - /********************* SQL ****************d*g**/ - - - - /** - * Delimites identifier for use in a SQL statement. - */ - public function delimite($name) - { - // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm - return '"' . str_replace('"', '""', $name) . '"'; - } - - - - /** - * Formats date-time for use in a SQL statement. - */ - public function formatDateTime(\DateTime $value) - { - return $value->format($this->fmtDateTime); - } - - - - /** - * Encodes string for use in a LIKE statement. - */ - public function formatLike($value, $pos) - { - throw new Nette\NotImplementedException; - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($offset > 0) { - // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html - $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' - . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') - . ') WHERE "__rnum" > '. (int) $offset; - - } elseif ($limit >= 0) { - $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; - } - } - - - - /** - * Normalizes result row. - */ - public function normalizeRow($row, $statement) - { - return $row; - } - -} +connection = $connection; + $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U'; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm + return '"' . str_replace('"', '""', $name) . '"'; + } + + + + /** + * Formats boolean for use in a SQL statement. + */ + public function formatBool($value) + { + return $value ? '1' : '0'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format($this->fmtDateTime); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + throw new Nette\NotImplementedException; + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($offset > 0) { + // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html + $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' + . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') + . ') WHERE "__rnum" > '. (int) $offset; + + } elseif ($limit >= 0) { + $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + $tables = array(); + foreach ($this->connection->query('SELECT * FROM cat') as $row) { + if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { + $tables[] = array( + 'name' => $row[0], + 'view' => $row[1] === 'VIEW', + ); + } + } + return $tables; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::SUPPORT_COLUMNS_META || $item === self::SUPPORT_SEQUENCE; + } + +} diff --git a/libs/Nette/Database/Drivers/OdbcDriver.php b/libs/Nette/Database/Drivers/OdbcDriver.php index 64676e6..743c759 100644 --- a/libs/Nette/Database/Drivers/OdbcDriver.php +++ b/libs/Nette/Database/Drivers/OdbcDriver.php @@ -1,100 +1,161 @@ - TRUE); - - /** @var Nette\Database\Connection */ - private $connection; - - - - public function __construct(Nette\Database\Connection $connection, array $options) - { - $this->connection = $connection; - } - - - - /********************* SQL ****************d*g**/ - - - - /** - * Delimites identifier for use in a SQL statement. - */ - public function delimite($name) - { - return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']'; - } - - - - /** - * Formats date-time for use in a SQL statement. - */ - public function formatDateTime(\DateTime $value) - { - return $value->format("#m/d/Y H:i:s#"); - } - - - - /** - * Encodes string for use in a LIKE statement. - */ - public function formatLike($value, $pos) - { - $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]')); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(&$sql, $limit, $offset) - { - // offset support is missing - if ($limit >= 0) { - $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; - } - - if ($offset) { - throw new Nette\InvalidArgumentException('Offset is not implemented in driver odbc.'); - } - } - - - - /** - * Normalizes result row. - */ - public function normalizeRow($row, $statement) - { - return $row; - } - -} +connection = $connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']'; + } + + + + /** + * Formats boolean for use in a SQL statement. + */ + public function formatBool($value) + { + return $value ? '1' : '0'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("#m/d/Y H:i:s#"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]')); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + // offset support is missing + if ($limit >= 0) { + $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; + } + + if ($offset) { + throw new Nette\InvalidArgumentException('Offset is not implemented in driver odbc.'); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::SUPPORT_COLUMNS_META; + } + +} diff --git a/libs/Nette/Database/Drivers/PgSqlDriver.php b/libs/Nette/Database/Drivers/PgSqlDriver.php index 207aa71..d7efd91 100644 --- a/libs/Nette/Database/Drivers/PgSqlDriver.php +++ b/libs/Nette/Database/Drivers/PgSqlDriver.php @@ -1,97 +1,254 @@ - TRUE); - - /** @var Nette\Database\Connection */ - private $connection; - - - - public function __construct(Nette\Database\Connection $connection, array $options) - { - $this->connection = $connection; - } - - - - /********************* SQL ****************d*g**/ - - - - /** - * Delimites identifier for use in a SQL statement. - */ - public function delimite($name) - { - // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS - return '"' . str_replace('"', '""', $name) . '"'; - } - - - - /** - * Formats date-time for use in a SQL statement. - */ - public function formatDateTime(\DateTime $value) - { - return $value->format("'Y-m-d H:i:s'"); - } - - - - /** - * Encodes string for use in a LIKE statement. - */ - public function formatLike($value, $pos) - { - throw new Nette\NotImplementedException; - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit >= 0) - $sql .= ' LIMIT ' . (int) $limit; - - if ($offset > 0) - $sql .= ' OFFSET ' . (int) $offset; - } - - - - /** - * Normalizes result row. - */ - public function normalizeRow($row, $statement) - { - return $row; - } - -} +connection = $connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + return '"' . str_replace('"', '""', $name) . '"'; + } + + + + /** + * Formats boolean for use in a SQL statement. + */ + public function formatBool($value) + { + return $value ? 'TRUE' : 'FALSE'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("'Y-m-d H:i:s'"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = strtr($value, array("'" => "''", '\\' => '\\\\', '%' => '\\\\%', '_' => '\\\\_')); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0) + $sql .= ' LIMIT ' . (int) $limit; + + if ($offset > 0) + $sql .= ' OFFSET ' . (int) $offset; + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + $tables = array(); + foreach ($this->connection->query(" + SELECT + table_name AS name, + table_type = 'VIEW' AS view + FROM + information_schema.tables + WHERE + table_schema = current_schema() + ") as $row) { + $tables[] = (array) $row; + } + + return $tables; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + $columns = array(); + foreach ($this->connection->query(" + SELECT + c.column_name AS name, + c.table_name AS table, + upper(c.udt_name) AS nativetype, + greatest(c.character_maximum_length, c.numeric_precision) AS size, + FALSE AS unsigned, + c.is_nullable = 'YES' AS nullable, + c.column_default AS default, + coalesce(tc.constraint_type = 'PRIMARY KEY', FALSE) AND strpos(c.column_default, 'nextval') = 1 AS autoincrement, + coalesce(tc.constraint_type = 'PRIMARY KEY', FALSE) AS primary, + substring(c.column_default from 'nextval[(]''\"?([^''\"]+)') AS sequence + FROM + information_schema.columns AS c + LEFT JOIN information_schema.constraint_column_usage AS ccu USING(table_catalog, table_schema, table_name, column_name) + LEFT JOIN information_schema.table_constraints AS tc USING(constraint_catalog, constraint_schema, constraint_name) + WHERE + c.table_name = {$this->connection->quote($table)} + AND + c.table_schema = current_schema() + AND + (tc.constraint_type IS NULL OR tc.constraint_type = 'PRIMARY KEY') + ORDER BY + c.ordinal_position + ") as $row) { + $column = (array) $row; + $column['vendor'] = $column; + unset($column['sequence']); + + $columns[] = $column; + } + + return $columns; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + /* There is no information about all indexes in information_schema, so pg catalog must be used */ + $indexes = array(); + foreach ($this->connection->query(" + SELECT + c2.relname AS name, + indisunique AS unique, + indisprimary AS primary, + attname AS column + FROM + pg_class AS c1 + JOIN pg_namespace ON c1.relnamespace = pg_namespace.oid + JOIN pg_index ON c1.oid = indrelid + JOIN pg_class AS c2 ON indexrelid = c2.oid + LEFT JOIN pg_attribute ON c1.oid = attrelid AND attnum = ANY(indkey) + WHERE + nspname = current_schema() + AND + c1.relkind = 'r' + AND + c1.relname = {$this->connection->quote($table)} + ") as $row) { + $indexes[$row['name']]['name'] = $row['name']; + $indexes[$row['name']]['unique'] = $row['unique']; + $indexes[$row['name']]['primary'] = $row['primary']; + $indexes[$row['name']]['columns'][] = $row['column']; + } + + return array_values($indexes); + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + /* Not for multi-column foreign keys */ + $keys = array(); + foreach ($this->connection->query(" + SELECT + tc.constraint_name AS name, + kcu.column_name AS local, + ccu.table_name AS table, + ccu.column_name AS foreign + FROM + information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu USING(constraint_catalog, constraint_schema, constraint_name) + JOIN information_schema.constraint_column_usage AS ccu USING(constraint_catalog, constraint_schema, constraint_name) + WHERE + constraint_type = 'FOREIGN KEY' + AND + tc.table_name = {$this->connection->quote($table)} + ORDER BY + kcu.ordinal_position + ") as $row) { + $keys[] = (array) $row; + } + + return $keys; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::SUPPORT_COLUMNS_META || $item === self::SUPPORT_SEQUENCE; + } + +} diff --git a/libs/Nette/Database/Drivers/Sqlite2Driver.php b/libs/Nette/Database/Drivers/Sqlite2Driver.php index 021bcd8..2db3d72 100644 --- a/libs/Nette/Database/Drivers/Sqlite2Driver.php +++ b/libs/Nette/Database/Drivers/Sqlite2Driver.php @@ -1,58 +1,68 @@ - $value) { - unset($row[$key]); - if ($key[0] === '[' || $key[0] === '"') { - $key = substr($key, 1, -1); - } - $row[$key] = $value; - } - return $row; - } - -} + $value) { + unset($row[$key]); + if ($key[0] === '[' || $key[0] === '"') { + $key = substr($key, 1, -1); + } + $row[$key] = $value; + } + return $row; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotSupportedException; // @see http://www.sqlite.org/foreignkeys.html + } + +} diff --git a/libs/Nette/Database/Drivers/SqliteDriver.php b/libs/Nette/Database/Drivers/SqliteDriver.php index fc80762..1ee5d47 100644 --- a/libs/Nette/Database/Drivers/SqliteDriver.php +++ b/libs/Nette/Database/Drivers/SqliteDriver.php @@ -1,99 +1,242 @@ - FALSE); - - /** @var Nette\Database\Connection */ - private $connection; - - /** @var string Datetime format */ - private $fmtDateTime; - - - - public function __construct(Nette\Database\Connection $connection, array $options) - { - $this->connection = $connection; - $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U'; - } - - - - /********************* SQL ****************d*g**/ - - - - /** - * Delimites identifier for use in a SQL statement. - */ - public function delimite($name) - { - return '[' . strtr($name, '[]', ' ') . ']'; - } - - - - /** - * Formats date-time for use in a SQL statement. - */ - public function formatDateTime(\DateTime $value) - { - return $value->format($this->fmtDateTime); - } - - - - /** - * Encodes string for use in a LIKE statement. - */ - public function formatLike($value, $pos) - { - $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\'); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; - } - - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(&$sql, $limit, $offset) - { - if ($limit >= 0 || $offset > 0) { - $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); - } - } - - - - /** - * Normalizes result row. - */ - public function normalizeRow($row, $statement) - { - return $row; - } - -} +connection = $connection; + $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U'; + //$connection->exec('PRAGMA foreign_keys = ON'); + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + return '[' . strtr($name, '[]', ' ') . ']'; + } + + + + /** + * Formats boolean for use in a SQL statement. + */ + public function formatBool($value) + { + return $value ? '1' : '0'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format($this->fmtDateTime); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\'); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0 || $offset > 0) { + $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + return $this->connection->query(" + SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view') + UNION ALL + SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') + ORDER BY name + ")->fetchAll(); + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + $meta = $this->connection->query(" + SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->connection->quote($table)} + UNION ALL + SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->connection->quote($table)} + ")->fetch(); + + $columns = array(); + foreach ($this->connection->query("PRAGMA table_info({$this->delimite($table)})") as $row) { + $column = $row['name']; + $pattern = "/(\"$column\"|\[$column\]|$column)\s+[^,]+\s+PRIMARY\s+KEY\s+AUTOINCREMENT/Ui"; + $type = explode('(', $row['type']); + $columns[] = array( + 'name' => $column, + 'table' => $table, + 'fullname' => "$table.$column", + 'nativetype' => strtoupper($type[0]), + 'size' => isset($type[1]) ? (int) $type[1] : NULL, + 'nullable' => $row['notnull'] == '0', + 'default' => $row['dflt_value'], + 'autoincrement' => (bool) preg_match($pattern, $meta['sql']), + 'primary' => $row['pk'] == '1', + 'vendor' => (array) $row, + ); + } + return $columns; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + $indexes = array(); + foreach ($this->connection->query("PRAGMA index_list({$this->delimite($table)})") as $row) { + $indexes[$row['name']]['name'] = $row['name']; + $indexes[$row['name']]['unique'] = (bool) $row['unique']; + } + + foreach ($indexes as $index => $values) { + $res = $this->connection->query("PRAGMA index_info({$this->delimite($index)})"); + while ($row = $res->fetch(TRUE)) { + $indexes[$index]['columns'][$row['seqno']] = $row['name']; + } + } + + $columns = $this->getColumns($table); + foreach ($indexes as $index => $values) { + $column = $indexes[$index]['columns'][0]; + $primary = FALSE; + foreach ($columns as $info) { + if ($column == $info['name']) { + $primary = $info['primary']; + break; + } + } + $indexes[$index]['primary'] = (bool) $primary; + } + if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid + foreach ($columns as $column) { + if ($column['vendor']['pk']) { + $indexes[] = array( + 'name' => 'ROWID', + 'unique' => TRUE, + 'primary' => TRUE, + 'columns' => array($column['name']), + ); + break; + } + } + } + + return array_values($indexes); + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + $keys = array(); + foreach ($this->connection->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) { + $keys[$row['id']]['name'] = $row['id']; // foreign key name + $keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns + $keys[$row['id']]['table'] = $row['table']; // referenced table + $keys[$row['id']]['foreign'][$row['seq']] = $row['to']; // referenced columns + $keys[$row['id']]['onDelete'] = $row['on_delete']; + $keys[$row['id']]['onUpdate'] = $row['on_update']; + + if ($keys[$row['id']]['foreign'][0] == NULL) { + $keys[$row['id']]['foreign'] = NULL; + } + } + return array_values($keys); + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return FALSE; + } + +} diff --git a/libs/Nette/Database/Helpers.php b/libs/Nette/Database/Helpers.php new file mode 100644 index 0000000..646447e --- /dev/null +++ b/libs/Nette/Database/Helpers.php @@ -0,0 +1,173 @@ + IReflection::FIELD_TEXT, // PostgreSQL arrays + 'BYTEA|BLOB|BIN' => IReflection::FIELD_BINARY, + 'TEXT|CHAR' => IReflection::FIELD_TEXT, + 'YEAR|BYTE|COUNTER|SERIAL|INT|LONG' => IReflection::FIELD_INTEGER, + 'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => IReflection::FIELD_FLOAT, + '^TIME$' => IReflection::FIELD_TIME, + 'TIME' => IReflection::FIELD_DATETIME, // DATETIME, TIMESTAMP + 'DATE' => IReflection::FIELD_DATE, + 'BOOL|BIT' => IReflection::FIELD_BOOL, + ); + + + + /** + * Displays complete result set as HTML table for debug purposes. + * @return void + */ + public static function dumpResult(Statement $statement) + { + echo "\n\n\n"; + if (!$statement->columnCount()) { + echo "\t\n\t\t\n\t\t\n\t\n
    " . htmlSpecialChars($statement->queryString) . "
    Affected rows:", $statement->rowCount(), "
    \n"; + return; + } + $i = 0; + foreach ($statement as $row) { + if ($i === 0) { + echo "\n\t\n\t\t#row\n"; + foreach ($row as $col => $foo) { + echo "\t\t" . htmlSpecialChars($col) . "\n"; + } + echo "\t\n\n\n"; + } + echo "\t\n\t\t", $i, "\n"; + foreach ($row as $col) { + //if (is_object($col)) $col = $col->__toString(); + echo "\t\t", htmlSpecialChars($col), "\n"; + } + echo "\t\n"; + $i++; + } + + if ($i === 0) { + echo "\t\n\t\tempty result set\n\t\n\n"; + } else { + echo "\n\n"; + } + } + + + + /** + * Returns syntax highlighted SQL command. + * @param string + * @return string + */ + public static function dumpSql($sql) + { + static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; + static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE'; + + // insert new lines + $sql = " $sql "; + $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); + + // reduce spaces + $sql = preg_replace('#[ \t]{2,}#', " ", $sql); + + $sql = wordwrap($sql, 100); + $sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql); + + // syntax highlight + $sql = htmlSpecialChars($sql); + $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", function($matches) { + if (!empty($matches[1])) // comment + return '' . $matches[1] . ''; + + if (!empty($matches[2])) // error + return '' . $matches[2] . ''; + + if (!empty($matches[3])) // most important keywords + return '' . $matches[3] . ''; + + if (!empty($matches[4])) // other keywords + return '' . $matches[4] . ''; + }, $sql); + + return '
    ' . trim($sql) . "
    \n"; + } + + + + /** + * Heuristic type detection. + * @param string + * @return string + * @internal + */ + public static function detectType($type) + { + static $cache; + if (!isset($cache[$type])) { + $cache[$type] = 'string'; + foreach (self::$typePatterns as $s => $val) { + if (preg_match("#$s#i", $type)) { + return $cache[$type] = $val; + } + } + } + return $cache[$type]; + } + + + + /** + * Import SQL dump from file - extreme fast. + * @return int count of commands + */ + public static function loadFromFile(Connection $connection, $file) + { + @set_time_limit(0); // intentionally @ + + $handle = @fopen($file, 'r'); // intentionally @ + if (!$handle) { + throw new Nette\FileNotFoundException("Cannot open file '$file'."); + } + + $count = 0; + $sql = ''; + while (!feof($handle)) { + $s = fgets($handle); + $sql .= $s; + if (substr(rtrim($s), -1) === ';') { + $connection->exec($sql); // native query without logging + $sql = ''; + $count++; + } + } + if (trim($sql) !== '') { + $connection->exec($sql); + $count++; + } + fclose($handle); + return $count; + } + +} diff --git a/libs/Nette/Database/IReflection.php b/libs/Nette/Database/IReflection.php new file mode 100644 index 0000000..454a1cd --- /dev/null +++ b/libs/Nette/Database/IReflection.php @@ -0,0 +1,68 @@ +, %2$s for table name + * @param string %1$s stands for key used after ->, %2$s for table name + */ + public function __construct($primary = 'id', $foreign = '%s_id', $table = '%s') + { + $this->primary = $primary; + $this->foreign = $foreign; + $this->table = $table; + } + + + + public function getPrimary($table) + { + return sprintf($this->primary, $this->getColumnFromTable($table)); + } + + + + public function getHasManyReference($table, $key) + { + $table = $this->getColumnFromTable($table); + return array( + sprintf($this->table, $key, $table), + sprintf($this->foreign, $table, $key), + ); + } + + + + public function getBelongsToReference($table, $key) + { + $table = $this->getColumnFromTable($table); + return array( + sprintf($this->table, $key, $table), + sprintf($this->foreign, $key, $table), + ); + } + + + + public function setConnection(Nette\Database\Connection $connection) + {} + + + + protected function getColumnFromTable($name) + { + if ($this->table !== '%s' && preg_match('(^' . str_replace('%s', '(.*)', preg_quote($this->table)) . '$)', $name, $match)) { + return $match[1]; + } + + return $name; + } + +} diff --git a/libs/Nette/Database/Reflection/DatabaseReflection.php b/libs/Nette/Database/Reflection/DatabaseReflection.php deleted file mode 100644 index dd6bef5..0000000 --- a/libs/Nette/Database/Reflection/DatabaseReflection.php +++ /dev/null @@ -1,117 +0,0 @@ -, %2$s for table name - * @param string %1$s stands for key used after ->, %2$s for table name - */ - public function __construct($primary = 'id', $foreign = '%s_id', $table = '%s') - { - $this->primary = $primary; - $this->foreign = $foreign; - $this->table = $table; - } - - - - public function getPrimary($table) - { - return sprintf($this->primary, $table); - } - - - - public function getReferencingColumn($name, $table) - { - return $this->getReferencedColumn($table, $name); - } - - - - public function getReferencedColumn($name, $table) - { - if ($this->table !== '%s' && preg_match('(^' . str_replace('%s', '(.*)', preg_quote($this->table)) . '$)', $name, $match)) { - $name = $match[1]; - } - return sprintf($this->foreign, $name, $table); - } - - - - public function getReferencedTable($name, $table) - { - return sprintf($this->table, $name, $table); - } - - - - /** - * Heuristic type detection. - * @param string - * @return string - * @internal - */ - public static function detectType($type) - { - static $types, $patterns = array( - 'BYTEA|BLOB|BIN' => self::FIELD_BINARY, - 'TEXT|CHAR' => self::FIELD_TEXT, - 'YEAR|BYTE|COUNTER|SERIAL|INT|LONG' => self::FIELD_INTEGER, - 'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => self::FIELD_FLOAT, - 'TIME|DATE' => self::FIELD_DATETIME, - 'BOOL|BIT' => self::FIELD_BOOL, - ); - - if (!isset($types[$type])) { - $types[$type] = 'string'; - foreach ($patterns as $s => $val) { - if (preg_match("#$s#i", $type)) { - return $types[$type] = $val; - } - } - } - return $types[$type]; - } - -} diff --git a/libs/Nette/Database/Reflection/DiscoveredReflection.php b/libs/Nette/Database/Reflection/DiscoveredReflection.php new file mode 100644 index 0000000..0c70d6f --- /dev/null +++ b/libs/Nette/Database/Reflection/DiscoveredReflection.php @@ -0,0 +1,196 @@ +cacheStorage = $storage; + } + + + + public function setConnection(Nette\Database\Connection $connection) + { + $this->connection = $connection; + if ($this->cacheStorage) { + $this->cache = new Nette\Caching\Cache($this->cacheStorage, 'Nette.Database.' . md5($connection->getDsn())); + $this->structure = $this->cache->load('structure') ?: $this->structure; + } + } + + + + public function __destruct() + { + if ($this->cache) { + $this->cache->save('structure', $this->structure); + } + } + + + + public function getPrimary($table) + { + $primary = & $this->structure['primary'][strtolower($table)]; + if (isset($primary)) { + return empty($primary) ? NULL : $primary; + } + + $columns = $this->connection->getSupplementalDriver()->getColumns($table); + $primaryCount = 0; + foreach ($columns as $column) { + if ($column['primary']) { + $primary = $column['name']; + $primaryCount++; + } + } + + if ($primaryCount !== 1) { + $primary = ''; + return NULL; + } + + return $primary; + } + + + + public function getHasManyReference($table, $key, $refresh = TRUE) + { + $reference = & $this->structure['hasMany'][strtolower($table)]; + if (!empty($reference)) { + $candidates = $columnCandidates = array(); + foreach ($reference as $targetPair) { + list($targetColumn, $targetTable) = $targetPair; + if (stripos($targetTable, $key) === FALSE) + continue; + + $candidates[] = array($targetTable, $targetColumn); + if (stripos($targetColumn, $table) !== FALSE) { + $columnCandidates[] = $candidate = array($targetTable, $targetColumn); + if (strtolower($targetTable) === strtolower($key)) + return $candidate; + } + } + + if (count($columnCandidates) === 1) { + return reset($columnCandidates); + } elseif (count($candidates) === 1) { + return reset($candidates); + } + + foreach ($candidates as $candidate) { + list($targetTable, $targetColumn) = $candidate; + if (strtolower($targetTable) === strtolower($key)) + return $candidate; + } + + if (!$refresh && !empty($candidates)) { + throw new \PDOException('Ambiguous joining column in related call.'); + } + } + + if (!$refresh) { + throw new \PDOException("No reference found for \${$table}->related({$key})."); + } + + $this->reloadAllForeignKeys(); + return $this->getHasManyReference($table, $key, FALSE); + } + + + + public function getBelongsToReference($table, $key, $refresh = TRUE) + { + $reference = & $this->structure['belongsTo'][strtolower($table)]; + if (!empty($reference)) { + foreach ($reference as $column => $targetTable) { + if (stripos($column, $key) !== FALSE) { + return array( + $targetTable, + $column, + ); + } + } + } + + if (!$refresh) { + throw new \PDOException("No reference found for \${$table}->{$key}."); + } + + $this->reloadForeignKeys($table); + return $this->getBelongsToReference($table, $key, FALSE); + } + + + + protected function reloadAllForeignKeys() + { + foreach ($this->connection->getSupplementalDriver()->getTables() as $table) { + if ($table['view'] == FALSE) { + $this->reloadForeignKeys($table['name']); + } + } + + foreach (array_keys($this->structure['hasMany']) as $table) { + uksort($this->structure['hasMany'][$table], function($a, $b) { + return strlen($a) - strlen($b); + }); + } + } + + + + protected function reloadForeignKeys($table) + { + foreach ($this->connection->getSupplementalDriver()->getForeignKeys($table) as $row) { + $this->structure['belongsTo'][strtolower($table)][$row['local']] = $row['table']; + $this->structure['hasMany'][strtolower($row['table'])][$row['local'] . $table] = array($row['local'], $table); + } + + if (isset($this->structure['belongsTo'][$table])) { + uksort($this->structure['belongsTo'][$table], function($a, $b) { + return strlen($a) - strlen($b); + }); + } + } + +} diff --git a/libs/Nette/Database/Row.php b/libs/Nette/Database/Row.php index c61366c..d72482d 100644 --- a/libs/Nette/Database/Row.php +++ b/libs/Nette/Database/Row.php @@ -1,47 +1,58 @@ -normalizeRow($this); - } - - - - /** - * Returns a item. - * @param mixed key or index - * @return mixed - */ - public function offsetGet($key) - { - if (is_int($key)) { - $arr = array_values((array) $this); - return $arr[$key]; - } - return $this->$key; - } - -} +normalizeRow($this); + } + + + + /** + * Returns a item. + * @param mixed key or index + * @return mixed + */ + public function offsetGet($key) + { + if (is_int($key)) { + $arr = array_values((array) $this); + return $arr[$key]; + } + return $this->$key; + } + + + + public function offsetExists($key) + { + if (is_int($key)) { + $arr = array_values((array) $this); + return isset($arr[$key]); + } + return parent::offsetExists($key); + } + +} diff --git a/libs/Nette/Database/SqlLiteral.php b/libs/Nette/Database/SqlLiteral.php index b5202c5..14144f7 100644 --- a/libs/Nette/Database/SqlLiteral.php +++ b/libs/Nette/Database/SqlLiteral.php @@ -1,34 +1,44 @@ -value = (string) $value; - } - -} +value = (string) $value; + } + + + + /** + * @return string + */ + public function __toString() + { + return $this->value; + } + +} diff --git a/libs/Nette/Database/SqlPreprocessor.php b/libs/Nette/Database/SqlPreprocessor.php index c65e647..3a21d0d 100644 --- a/libs/Nette/Database/SqlPreprocessor.php +++ b/libs/Nette/Database/SqlPreprocessor.php @@ -1,167 +1,162 @@ -connection = $connection; - $this->driver = $connection->getSupplementalDriver(); - } - - - - /** - * @param string - * @param array - * @return array of [sql, params] - */ - public function process($sql, $params) - { - $this->params = $params; - $this->counter = 0; - $this->remaining = array(); - - $cmd = strtoupper(substr(ltrim($sql), 0, 6)); // detect array mode - $this->arrayMode = $cmd === 'INSERT' || $cmd === 'REPLAC' ? 'values' : 'assoc'; - - /*~ - \'.*?\'|".*?"| ## string - :[a-zA-Z0-9_]+:| ## :substitution: - \? ## placeholder - ~xs*/ - $sql = Nette\Utils\Strings::replace($sql, '~\'.*?\'|".*?"|:[a-zA-Z0-9_]+:|\?~s', array($this, 'callback')); - - while ($this->counter < count($params)) { - $sql .= ' ' . $this->formatValue($params[$this->counter++]); - } - - return array($sql, $this->remaining); - } - - - - /** @internal */ - public function callback($m) - { - $m = $m[0]; - if ($m[0] === "'" || $m[0] === '"') { // string - return $m; - - } elseif ($m[0] === '?') { // placeholder - return $this->formatValue($this->params[$this->counter++]); - - } elseif ($m[0] === ':') { // substitution - $s = substr($m, 1, -1); - return isset($this->connection->substitutions[$s]) ? $this->connection->substitutions[$s] : $m; - } - } - - - - private function formatValue($value) - { - if (is_string($value)) { - if (strlen($value) > 20) { - $this->remaining[] = $value; - return '?'; - - } else { - return $this->connection->quote($value); - } - - } elseif (is_int($value)) { - return (string) $value; - - } elseif (is_float($value)) { - return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); - - } elseif (is_bool($value)) { - $this->remaining[] = $value; - return '?'; - - } elseif ($value === NULL) { - return 'NULL'; - - } elseif (is_array($value) || $value instanceof \Traversable) { - $vx = $kx = array(); - - if (isset($value[0])) { // non-associative; value, value, value - foreach ($value as $v) { - $vx[] = $this->formatValue($v); - } - return implode(', ', $vx); - - } elseif ($this->arrayMode === 'values') { // (key, key, ...) VALUES (value, value, ...) - $this->arrayMode = 'multi'; - foreach ($value as $k => $v) { - $kx[] = $this->driver->delimite($k); - $vx[] = $this->formatValue($v); - } - return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; - - } elseif ($this->arrayMode === 'assoc') { // key=value, key=value, ... - foreach ($value as $k => $v) { - $vx[] = $this->driver->delimite($k) . '=' . $this->formatValue($v); - } - return implode(', ', $vx); - - } elseif ($this->arrayMode === 'multi') { // multiple insert (value, value, ...), ... - foreach ($value as $k => $v) { - $vx[] = $this->formatValue($v); - } - return ', (' . implode(', ', $vx) . ')'; - } - - } elseif ($value instanceof \DateTime) { - return $this->driver->formatDateTime($value); - - } elseif ($value instanceof SqlLiteral) { - return $value->value; - - } else { - $this->remaining[] = $value; - return '?'; - } - } - -} +connection = $connection; + $this->driver = $connection->getSupplementalDriver(); + } + + + + /** + * @param string + * @param array + * @return array of [sql, params] + */ + public function process($sql, $params) + { + $this->params = $params; + $this->counter = 0; + $this->remaining = array(); + $this->arrayMode = 'assoc'; + + $sql = Nette\Utils\Strings::replace($sql, '~\'.*?\'|".*?"|\?|\b(?:INSERT|REPLACE|UPDATE)\b~si', array($this, 'callback')); + + while ($this->counter < count($params)) { + $sql .= ' ' . $this->formatValue($params[$this->counter++]); + } + + return array($sql, $this->remaining); + } + + + + /** @internal */ + public function callback($m) + { + $m = $m[0]; + if ($m[0] === "'" || $m[0] === '"') { // string + return $m; + + } elseif ($m === '?') { // placeholder + return $this->formatValue($this->params[$this->counter++]); + + } else { // INSERT, REPLACE, UPDATE + $this->arrayMode = strtoupper($m) === 'UPDATE' ? 'assoc' : 'values'; + return $m; + } + } + + + + private function formatValue($value) + { + if (is_string($value)) { + if (strlen($value) > 20) { + $this->remaining[] = $value; + return '?'; + + } else { + return $this->connection->quote($value); + } + + } elseif (is_int($value)) { + return (string) $value; + + } elseif (is_float($value)) { + return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); + + } elseif (is_bool($value)) { + return $this->driver->formatBool($value); + + } elseif ($value === NULL) { + return 'NULL'; + + } elseif ($value instanceof Table\ActiveRow) { + return $value->getPrimary(); + + } elseif (is_array($value) || $value instanceof \Traversable) { + $vx = $kx = array(); + + if (isset($value[0])) { // non-associative; value, value, value + foreach ($value as $v) { + $vx[] = $this->formatValue($v); + } + return implode(', ', $vx); + + } elseif ($this->arrayMode === 'values') { // (key, key, ...) VALUES (value, value, ...) + $this->arrayMode = 'multi'; + foreach ($value as $k => $v) { + $kx[] = $this->driver->delimite($k); + $vx[] = $this->formatValue($v); + } + return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; + + } elseif ($this->arrayMode === 'assoc') { // key=value, key=value, ... + foreach ($value as $k => $v) { + $vx[] = $this->driver->delimite($k) . '=' . $this->formatValue($v); + } + return implode(', ', $vx); + + } elseif ($this->arrayMode === 'multi') { // multiple insert (value, value, ...), ... + foreach ($value as $k => $v) { + $vx[] = $this->formatValue($v); + } + return '(' . implode(', ', $vx) . ')'; + } + + } elseif ($value instanceof \DateTime) { + return $this->driver->formatDateTime($value); + + } elseif ($value instanceof SqlLiteral) { + return $value->__toString(); + + } else { + $this->remaining[] = $value; + return '?'; + } + } + +} diff --git a/libs/Nette/Database/Statement.php b/libs/Nette/Database/Statement.php index 06ccbd2..68c36bf 100644 --- a/libs/Nette/Database/Statement.php +++ b/libs/Nette/Database/Statement.php @@ -1,225 +1,221 @@ -connection = $connection; - $this->setFetchMode(PDO::FETCH_CLASS, 'Nette\Database\Row', array($this)); - } - - - - /** - * @return Connection - */ - public function getConnection() - { - return $this->connection; - } - - - - /** - * Executes statement. - * @param array - * @return Statement provides a fluent interface - */ - public function execute($params = array()) - { - static $types = array('boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, - 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL); - - foreach ($params as $key => $value) { - $type = gettype($value); - $this->bindValue(is_int($key) ? $key + 1 : $key, $value, isset($types[$type]) ? $types[$type] : PDO::PARAM_STR); - } - - $time = microtime(TRUE); - try { - parent::execute(); - } catch (\PDOException $e) { - $e->queryString = $this->queryString; - throw $e; - } - $this->time = microtime(TRUE) - $time; - $this->connection->__call('onQuery', array($this, $params)); // $this->connection->onQuery() in PHP 5.3 - - return $this; - } - - - - /** - * Fetches into an array where the 1st column is a key and all subsequent columns are values. - * @return array - */ - public function fetchPairs() - { - return $this->fetchAll(PDO::FETCH_KEY_PAIR); // since PHP 5.2.3 - } - - - - /** - * Normalizes result row. - * @param array - * @return array - */ - public function normalizeRow($row) - { - if ($this->types === NULL) { - $this->types = array(); - if ($this->connection->getSupplementalDriver()->supports['meta']) { // workaround for PHP bugs #53782, #54695 - foreach ($row as $key => $foo) { - $type = $this->getColumnMeta(count($this->types)); - if (isset($type['native_type'])) { - $this->types[$key] = Reflection\DatabaseReflection::detectType($type['native_type']); - } - } - } - } - - foreach ($this->types as $key => $type) { - $value = $row[$key]; - if ($value === NULL || $value === FALSE || $type === Reflection\DatabaseReflection::FIELD_TEXT) { - - } elseif ($type === Reflection\DatabaseReflection::FIELD_INTEGER) { - $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp; - - } elseif ($type === Reflection\DatabaseReflection::FIELD_FLOAT) { - $row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value; - - } elseif ($type === Reflection\DatabaseReflection::FIELD_BOOL) { - $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; - } - } - - return $this->connection->getSupplementalDriver()->normalizeRow($row, $this); - } - - - - /********************* misc tools ****************d*g**/ - - - - /** - * Displays complete result set as HTML table for debug purposes. - * @return void - */ - public function dump() - { - echo "\n\n\n"; - if (!$this->columnCount()) { - echo "\t\n\t\t\n\t\t\n\t\n
    " . htmlSpecialChars($this->queryString) . "
    Affected rows:", $this->rowCount(), "
    \n"; - return; - } - $i = 0; - foreach ($this as $row) { - if ($i === 0) { - echo "\n\t\n\t\t#row\n"; - foreach ($row as $col => $foo) { - echo "\t\t" . htmlSpecialChars($col) . "\n"; - } - echo "\t\n\n\n"; - } - echo "\t\n\t\t", $i, "\n"; - foreach ($row as $col) { - //if (is_object($col)) $col = $col->__toString(); - echo "\t\t", htmlSpecialChars($col), "\n"; - } - echo "\t\n"; - $i++; - } - - if ($i === 0) { - echo "\t\n\t\tempty result set\n\t\n\n"; - } else { - echo "\n\n"; - } - } - - - - /********************* Nette\Object behaviour ****************d*g**/ - - - - /** - * @return Nette\Reflection\ClassType - */ - public static function getReflection() - { - return new Nette\Reflection\ClassType(get_called_class()); - } - - - - public function __call($name, $args) - { - return ObjectMixin::call($this, $name, $args); - } - - - - public function &__get($name) - { - return ObjectMixin::get($this, $name); - } - - - - public function __set($name, $value) - { - return ObjectMixin::set($this, $name, $value); - } - - - - public function __isset($name) - { - return ObjectMixin::has($this, $name); - } - - - - public function __unset($name) - { - ObjectMixin::remove($this, $name); - } - -} +connection = $connection; + $this->setFetchMode(PDO::FETCH_CLASS, 'Nette\Database\Row', array($this)); + } + + + + /** + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } + + + + /** + * Executes statement. + * @param array + * @return Statement provides a fluent interface + */ + public function execute($params = array()) + { + static $types = array('boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, + 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL); + + foreach ($params as $key => $value) { + $type = gettype($value); + $this->bindValue(is_int($key) ? $key + 1 : $key, $value, isset($types[$type]) ? $types[$type] : PDO::PARAM_STR); + } + + $time = microtime(TRUE); + try { + parent::execute(); + } catch (\PDOException $e) { + $e->queryString = $this->queryString; + throw $e; + } + $this->time = microtime(TRUE) - $time; + $this->connection->__call('onQuery', array($this, $params)); // $this->connection->onQuery() in PHP 5.3 + + return $this; + } + + + + /** + * Fetches into an array where the 1st column is a key and all subsequent columns are values. + * @return array + */ + public function fetchPairs() + { + return $this->fetchAll(PDO::FETCH_KEY_PAIR); // since PHP 5.2.3 + } + + + + /** + * Normalizes result row. + * @param array + * @return array + */ + public function normalizeRow($row) + { + foreach ($this->detectColumnTypes() as $key => $type) { + $value = $row[$key]; + if ($value === NULL || $value === FALSE || $type === IReflection::FIELD_TEXT) { + + } elseif ($type === IReflection::FIELD_INTEGER) { + $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp; + + } elseif ($type === IReflection::FIELD_FLOAT) { + $row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value; + + } elseif ($type === IReflection::FIELD_BOOL) { + $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; + + } elseif ($type === IReflection::FIELD_DATETIME || $type === IReflection::FIELD_DATE || $type === IReflection::FIELD_TIME) { + $row[$key] = new Nette\DateTime($value); + + } + } + + return $this->connection->getSupplementalDriver()->normalizeRow($row, $this); + } + + + + private function detectColumnTypes() + { + if ($this->types === NULL) { + $this->types = array(); + if ($this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_COLUMNS_META)) { // workaround for PHP bugs #53782, #54695 + $col = 0; + while ($meta = $this->getColumnMeta($col++)) { + if (isset($meta['native_type'])) { + $this->types[$meta['name']] = Helpers::detectType($meta['native_type']); + } + } + } + } + return $this->types; + } + + + + /** + * @return float + */ + public function getTime() + { + return $this->time; + } + + + + /********************* misc tools ****************d*g**/ + + + + /** + * Displays complete result set as HTML table for debug purposes. + * @return void + */ + public function dump() + { + Helpers::dumpResult($this); + } + + + + /********************* Nette\Object behaviour ****************d*g**/ + + + + /** + * @return Nette\Reflection\ClassType + */ + public /**/static/**/ function getReflection() + { + return new Nette\Reflection\ClassType(/*5.2*$this*//**/get_called_class()/**/); + } + + + + public function __call($name, $args) + { + return ObjectMixin::call($this, $name, $args); + } + + + + public function &__get($name) + { + return ObjectMixin::get($this, $name); + } + + + + public function __set($name, $value) + { + return ObjectMixin::set($this, $name, $value); + } + + + + public function __isset($name) + { + return ObjectMixin::has($this, $name); + } + + + + public function __unset($name) + { + ObjectMixin::remove($this, $name); + } + +} diff --git a/libs/Nette/Database/Table/ActiveRow.php b/libs/Nette/Database/Table/ActiveRow.php index 7cbccf0..9285295 100644 --- a/libs/Nette/Database/Table/ActiveRow.php +++ b/libs/Nette/Database/Table/ActiveRow.php @@ -1,265 +1,316 @@ -data = $data; - $this->table = $table; - } - - - - /** - * Returns primary key value. - * @return string - */ - public function __toString() - { - return (string) $this[$this->table->primary]; // (string) - PostgreSQL returns int - } - - - - /** - * @return array - */ - public function toArray() - { - $this->access(NULL); - return $this->data; - } - - - - /** - * Returns referenced row. - * @param string - * @return ActiveRow or NULL if the row does not exist - */ - public function ref($name) - { - $referenced = $this->table->getReferencedTable($name, $column); - if (isset($referenced[$this[$column]])) { // referenced row may not exist - $res = $referenced[$this[$column]]; - return $res; - } - } - - - - /** - * Returns referencing rows. - * @param string table name - * @return GroupedSelection - */ - public function related($table) - { - $referencing = $this->table->getReferencingTable($table); - $referencing->active = $this[$this->table->primary]; - return $referencing; - } - - - - /** - * Updates row. - * @param array or NULL for all modified values - * @return int number of affected rows or FALSE in case of an error - */ - public function update($data = NULL) - { - if ($data === NULL) { - $data = $this->modified; - } - return $this->table->connection->table($this->table->name) - ->where($this->table->primary, $this[$this->table->primary]) - ->update($data); - } - - - - /** - * Deletes row. - * @return int number of affected rows or FALSE in case of an error - */ - public function delete() - { - return $this->table->connection->table($this->table->name) - ->where($this->table->primary, $this[$this->table->primary]) - ->delete(); - } - - - - /********************* interface IteratorAggregate ****************d*g**/ - - - - public function getIterator() - { - $this->access(NULL); - return new \ArrayIterator($this->data); - } - - - - /********************* interface ArrayAccess & magic accessors ****************d*g**/ - - - - /** - * Stores value in column. - * @param string column name - * @return NULL - */ - public function offsetSet($key, $value) - { - $this->__set($key, $value); - } - - - - /** - * Returns value of column. - * @param string column name - * @return string - */ - public function offsetGet($key) - { - return $this->__get($key); - } - - - - /** - * Tests if column exists. - * @param string column name - * @return bool - */ - public function offsetExists($key) - { - return $this->__isset($key); - } - - - - /** - * Removes column from data. - * @param string column name - * @return NULL - */ - public function offsetUnset($key) - { - $this->__unset($key); - } - - - - public function __set($key, $value) - { - $this->data[$key] = $value; - $this->modified[$key] = $value; - } - - - - public function &__get($key) - { - if (array_key_exists($key, $this->data)) { - $this->access($key); - return $this->data[$key]; - } - - $column = $this->table->connection->databaseReflection->getReferencedColumn($key, $this->table->name); - if (array_key_exists($column, $this->data)) { - $value = $this->data[$column]; - $referenced = $this->table->getReferencedTable($key); - $ret = isset($referenced[$value]) ? $referenced[$value] : NULL; // referenced row may not exist - return $ret; - } - - $this->access($key); - if (array_key_exists($key, $this->data)) { - return $this->data[$key]; - - } else { - $this->access($key, TRUE); - - $this->access($column); - if (array_key_exists($column, $this->data)) { - $value = $this->data[$column]; - $referenced = $this->table->getReferencedTable($key); - $ret = isset($referenced[$value]) ? $referenced[$value] : NULL; // referenced row may not exist - - } else { - $this->access($column, TRUE); - trigger_error("Unknown column $key", E_USER_WARNING); - $ret = NULL; - } - return $ret; - } - } - - - - public function __isset($key) - { - $this->access($key); - $return = array_key_exists($key, $this->data); - if (!$return) { - $this->access($key, TRUE); - } - return $return; - } - - - - public function __unset($key) - { - unset($this->data[$key]); - unset($this->modified[$key]); - } - - - - public function access($key, $delete = FALSE) - { - if ($this->table->connection->cache && $this->table->access($key, $delete)) { - $this->data = $this->table[$this->data[$this->table->primary]]->data; - } - } - -} +data = $data; + $this->table = $table; + } + + + + /** + * @internal + * @ignore + */ + public function setTable(Selection $table) + { + $this->table = $table; + } + + + + /** + * @internal + * @ignore + */ + public function getTable() + { + return $this->table; + } + + + + public function __toString() + { + try { + return (string) $this->getPrimary(); + } catch (\Exception $e) { + trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR); + } + } + + + + /** + * @return array + */ + public function toArray() + { + $this->access(NULL); + return $this->data; + } + + + + /** + * Returns primary key value. + * @return mixed + */ + public function getPrimary() + { + if (!isset($this->data[$this->table->getPrimary()])) { + throw new Nette\NotSupportedException("Table {$this->table->getName()} does not have any primary key."); + } + return $this[$this->table->getPrimary()]; + } + + + + /** + * Returns referenced row. + * @param string + * @param string + * @return ActiveRow or NULL if the row does not exist + */ + public function ref($key, $throughColumn = NULL) + { + if (!$throughColumn) { + list($key, $throughColumn) = $this->table->getConnection()->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key); + } + + return $this->getReference($key, $throughColumn); + } + + + + /** + * Returns referencing rows. + * @param string + * @param string + * @return GroupedSelection + */ + public function related($key, $throughColumn = NULL) + { + if (strpos($key, '.') !== FALSE) { + list($key, $throughColumn) = explode('.', $key); + } elseif (!$throughColumn) { + list($key, $throughColumn) = $this->table->getConnection()->getDatabaseReflection()->getHasManyReference($this->table->getName(), $key); + } + + return $this->table->getReferencingTable($key, $throughColumn, $this[$this->table->getPrimary()]); + } + + + + /** + * Updates row. + * @param array or NULL for all modified values + * @return int number of affected rows or FALSE in case of an error + */ + public function update($data = NULL) + { + if ($data === NULL) { + $data = $this->modified; + } + return $this->table->getConnection()->table($this->table->getName()) + ->where($this->table->getPrimary(), $this[$this->table->getPrimary()]) + ->update($data); + } + + + + /** + * Deletes row. + * @return int number of affected rows or FALSE in case of an error + */ + public function delete() + { + return $this->table->getConnection()->table($this->table->getName()) + ->where($this->table->getPrimary(), $this[$this->table->getPrimary()]) + ->delete(); + } + + + + /********************* interface IteratorAggregate ****************d*g**/ + + + + public function getIterator() + { + $this->access(NULL); + return new \ArrayIterator($this->data); + } + + + + /********************* interface ArrayAccess & magic accessors ****************d*g**/ + + + + /** + * Stores value in column. + * @param string column name + * @param string value + * @return void + */ + public function offsetSet($key, $value) + { + $this->__set($key, $value); + } + + + + /** + * Returns value of column. + * @param string column name + * @return string + */ + public function offsetGet($key) + { + return $this->__get($key); + } + + + + /** + * Tests if column exists. + * @param string column name + * @return bool + */ + public function offsetExists($key) + { + return $this->__isset($key); + } + + + + /** + * Removes column from data. + * @param string column name + * @return void + */ + public function offsetUnset($key) + { + $this->__unset($key); + } + + + + public function __set($key, $value) + { + $this->data[$key] = $value; + $this->modified[$key] = $value; + } + + + + public function &__get($key) + { + $this->access($key); + if (array_key_exists($key, $this->data)) { + return $this->data[$key]; + } + + list($table, $column) = $this->table->getConnection()->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key); + $referenced = $this->getReference($table, $column); + if ($referenced !== FALSE) { + $this->access($key, FALSE); + return $referenced; + } + + $this->access($key, NULL); + throw new Nette\MemberAccessException("Cannot read an undeclared column \"$key\"."); + } + + + + public function __isset($key) + { + $this->access($key); + if (array_key_exists($key, $this->data)) { + return isset($this->data[$key]); + } + $this->access($key, NULL); + return FALSE; + } + + + + public function __unset($key) + { + unset($this->data[$key]); + unset($this->modified[$key]); + } + + + + /** + * @internal + */ + public function access($key, $cache = TRUE) + { + if ($this->table->getConnection()->getCache() && !isset($this->modified[$key]) && $this->table->access($key, $cache)) { + $id = (isset($this->data[$this->table->getPrimary()]) ? $this->data[$this->table->getPrimary()] : $this->data); + $this->data = $this->table[$id]->data; + } + } + + + + protected function getReference($table, $column) + { + if (array_key_exists($column, $this->data)) { + $this->access($column); + + $value = $this->data[$column]; + $value = $value instanceof ActiveRow ? $value->getPrimary() : $value; + + $referenced = $this->table->getReferencedTable($table, $column, !empty($this->modified[$column])); + $referenced = isset($referenced[$value]) ? $referenced[$value] : NULL; // referenced row may not exist + + if (!empty($this->modified[$column])) { // cause saving changed column and prevent regenerating referenced table for $column + $this->modified[$column] = 0; // 0 fails on empty, pass on isset + } + + return $referenced; + } + + return FALSE; + } + +} diff --git a/libs/Nette/Database/Table/GroupedSelection.php b/libs/Nette/Database/Table/GroupedSelection.php index b0b18e0..7e11a6c 100644 --- a/libs/Nette/Database/Table/GroupedSelection.php +++ b/libs/Nette/Database/Table/GroupedSelection.php @@ -1,179 +1,257 @@ -connection); - $this->refTable = $refTable; - $this->through($column); - } - - - - /** - * Specify referencing column. - * @param string - * @return GroupedSelection provides a fluent interface - */ - public function through($column) - { - $this->column = $column; - $this->delimitedColumn = $this->refTable->connection->getSupplementalDriver()->delimite($this->column); - return $this; - } - - - - public function select($columns) - { - if (!$this->select) { - $this->select[] = "$this->delimitedName.$this->delimitedColumn"; - } - return parent::select($columns); - } - - - - public function order($columns) - { - if (!$this->order) { // improve index utilization - $this->order[] = "$this->delimitedName.$this->delimitedColumn" - . (preg_match('~\\bDESC$~i', $columns) ? ' DESC' : ''); - } - return parent::order($columns); - } - - - - public function aggregation($function) - { - $join = $this->createJoins(implode(',', $this->conditions), TRUE) + $this->createJoins($function); - $column = ($join ? "$this->table." : '') . $this->column; - $query = "SELECT $function, $this->delimitedColumn FROM $this->delimitedName" . implode($join); - if ($this->where) { - $query .= ' WHERE (' . implode(') AND (', $this->where) . ')'; - } - $query .= " GROUP BY $this->delimitedColumn"; - $aggregation = & $this->refTable->aggregation[$query]; - if ($aggregation === NULL) { - $aggregation = array(); - foreach ($this->query($query, $this->parameters) as $row) { - $aggregation[$row[$this->column]] = $row; - } - } - - foreach ($aggregation[$this->active] as $val) { - return $val; - } - } - - - - public function insert($data) - { - if ($data instanceof \Traversable && !$data instanceof Selection) { - $data = iterator_to_array($data); - } - if (is_array($data)) { - $data[$this->column] = $this->active; - } - return parent::insert($data); - } - - - - public function update($data) - { - $where = $this->where; - $this->where[0] = "$this->delimitedColumn = " . $this->connection->quote($this->active); - $return = parent::update($data); - $this->where = $where; - return $return; - } - - - - public function delete() - { - $where = $this->where; - $this->where[0] = "$this->delimitedColumn = " . $this->connection->quote($this->active); - $return = parent::delete(); - $this->where = $where; - return $return; - } - - - - protected function execute() - { - if ($this->rows !== NULL) { - return; - } - - $referencing = & $this->refTable->referencing[$this->getSql()]; - if ($referencing === NULL) { - $limit = $this->limit; - $rows = count($this->refTable->rows); - if ($this->limit && $rows > 1) { - $this->limit = NULL; - } - parent::execute(); - $this->limit = $limit; - $referencing = array(); - $offset = array(); - foreach ($this->rows as $key => $row) { - $ref = & $referencing[$row[$this->column]]; - $skip = & $offset[$row[$this->column]]; - if ($limit === NULL || $rows <= 1 || (count($ref) < $limit && $skip >= $this->offset)) { - $ref[$key] = $row; - } else { - unset($this->rows[$key]); - } - $skip++; - unset($ref, $skip); - } - } - - $this->data = & $referencing[$this->active]; - if ($this->data === NULL) { - $this->data = array(); - } - } - -} +connection); + $this->refTable = $refTable; + $this->column = $column; + } + + + + /** + * Sets active group. + * @internal + * @param int primary key of grouped rows + * @return GroupedSelection + */ + public function setActive($active) + { + $this->active = $active; + return $this; + } + + + + /** @deprecated */ + public function through($column) + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::related("' . $this->name . '", "' . $column . '") instead.', E_USER_WARNING); + $this->column = $column; + $this->delimitedColumn = $this->refTable->connection->getSupplementalDriver()->delimite($this->column); + return $this; + } + + + + public function select($columns) + { + if (!$this->sqlBuilder->getSelect()) { + $this->sqlBuilder->addSelect("$this->name.$this->column"); + } + + return parent::select($columns); + } + + + + public function order($columns) + { + if (!$this->sqlBuilder->getOrder()) { + // improve index utilization + $this->sqlBuilder->addOrder("$this->name.$this->column" . (preg_match('~\\bDESC$~i', $columns) ? ' DESC' : '')); + } + + return parent::order($columns); + } + + + + /********************* aggregations ****************d*g**/ + + + + public function aggregation($function) + { + $aggregation = & $this->getRefTable($refPath)->aggregation[$refPath . $function . $this->sqlBuilder->buildSelectQuery() . json_encode($this->sqlBuilder->getParameters())]; + + if ($aggregation === NULL) { + $aggregation = array(); + + $selection = $this->createSelectionInstance(); + $selection->getSqlBuilder()->importConditions($this->getSqlBuilder()); + $selection->select($function); + $selection->select("$this->name.$this->column"); + $selection->group("$this->name.$this->column"); + + foreach ($selection as $row) { + $aggregation[$row[$this->column]] = $row; + } + } + + if (isset($aggregation[$this->active])) { + foreach ($aggregation[$this->active] as $val) { + return $val; + } + } + } + + + + public function count($column = NULL) + { + $return = parent::count($column); + return isset($return) ? $return : 0; + } + + + + /********************* internal ****************d*g**/ + + + + protected function execute() + { + if ($this->rows !== NULL) { + return; + } + + $hash = md5($this->sqlBuilder->buildSelectQuery() . json_encode($this->sqlBuilder->getParameters())); + + $referencing = & $this->getRefTable($refPath)->referencing[$refPath . $hash]; + $this->rows = & $referencing['rows']; + $this->referenced = & $referencing['refs']; + $this->accessed = & $referencing['accessed']; + $refData = & $referencing['data']; + + if ($refData === NULL) { + $limit = $this->sqlBuilder->getLimit(); + $rows = count($this->refTable->rows); + if ($limit && $rows > 1) { + $this->sqlBuilder->setLimit(NULL, NULL); + } + parent::execute(); + $this->sqlBuilder->setLimit($limit, NULL); + $refData = array(); + $offset = array(); + foreach ($this->rows as $key => $row) { + $ref = & $refData[$row[$this->column]]; + $skip = & $offset[$row[$this->column]]; + if ($limit === NULL || $rows <= 1 || (count($ref) < $limit && $skip >= $this->sqlBuilder->getOffset())) { + $ref[$key] = $row; + } else { + unset($this->rows[$key]); + } + $skip++; + unset($ref, $skip); + } + } + + $this->data = & $refData[$this->active]; + if ($this->data === NULL) { + $this->data = array(); + } else { + foreach ($this->data as $row) { + $row->setTable($this); // injects correct parent GroupedSelection + } + reset($this->data); + } + } + + + + protected function getRefTable(& $refPath) + { + $refObj = $this->refTable; + $refPath = $this->name . '.'; + while ($refObj instanceof GroupedSelection) { + $refPath .= $refObj->name . '.'; + $refObj = $refObj->refTable; + } + + return $refObj; + } + + + + /********************* manipulation ****************d*g**/ + + + + public function insert($data) + { + if ($data instanceof \Traversable && !$data instanceof Selection) { + $data = iterator_to_array($data); + } + + if (Nette\Utils\Validators::isList($data)) { + foreach (array_keys($data) as $key) { + $data[$key][$this->column] = $this->active; + } + } else { + $data[$this->column] = $this->active; + } + + return parent::insert($data); + } + + + + public function update($data) + { + $builder = $this->sqlBuilder; + + $this->sqlBuilder = new SqlBuilder($this); + $this->where($this->column, $this->active); + $return = parent::update($data); + + $this->sqlBuilder = $builder; + return $return; + } + + + + public function delete() + { + $builder = $this->sqlBuilder; + + $this->sqlBuilder = new SqlBuilder($this); + $this->where($this->column, $this->active); + $return = parent::delete(); + + $this->sqlBuilder = $builder; + return $return; + } + +} diff --git a/libs/Nette/Database/Table/Selection.php b/libs/Nette/Database/Table/Selection.php index 37d3b1d..edf505e 100644 --- a/libs/Nette/Database/Table/Selection.php +++ b/libs/Nette/Database/Table/Selection.php @@ -1,783 +1,827 @@ - TableRow] readed from database */ - protected $rows; - - /** @var array of [primary key => TableRow] modifiable */ - protected $data; - - /** @var array of column to select */ - protected $select = array(); - - /** @var array of where conditions */ - protected $where = array(); - - /** @var array of where conditions for caching */ - protected $conditions = array(); - - /** @var array of parameters passed to where conditions */ - protected $parameters = array(); - - /** @var array or columns to order by */ - protected $order = array(); - - /** @var int number of rows to fetch */ - protected $limit = NULL; - - /** @var int first row to fetch */ - protected $offset = NULL; - - /** @var string columns to grouping */ - protected $group = ''; - - /** @var string grouping condition */ - protected $having = ''; - - /** @var array of referenced TableSelection */ - protected $referenced = array(); - - /** @var array of [sql => [column => [key => TableRow]]] used by GroupedTableSelection */ - protected $referencing = array(); - - /** @var array of [sql => [key => TableRow]] used by GroupedTableSelection */ - protected $aggregation = array(); - - /** @var array of touched columns */ - protected $accessed; - - /** @var array of earlier touched columns */ - protected $prevAccessed; - - /** @var array of primary key values */ - protected $keys = array(); - - /** @var string */ - protected $delimitedName; - - /** @var string */ - protected $delimitedPrimary; - - - - /** - * @param string - * @param - */ - public function __construct($table, Nette\Database\Connection $connection) - { - $this->name = $table; - $this->connection = $connection; - $this->primary = $this->getPrimary($table); - $this->delimitedName = $connection->getSupplementalDriver()->delimite($this->name); - $this->delimitedPrimary = $connection->getSupplementalDriver()->delimite($this->primary); - } - - - - /** - * Saves data to cache and empty result. - */ - public function __destruct() - { - if ($this->connection->cache && !$this->select && $this->rows !== NULL) { - $accessed = $this->accessed; - if (is_array($accessed)) { - $accessed = array_filter($accessed); - } - $this->connection->cache->save(array(__CLASS__, $this->name, $this->conditions), $accessed); - } - $this->rows = NULL; - } - - - - /** - * Returns row specified by primary key. - * @param mixed - * @return ActiveRow or NULL if there is no such row - */ - public function get($key) - { - // can also use array_pop($this->where) instead of clone to save memory - $clone = clone $this; - $clone->where($this->delimitedPrimary, $key); - return $clone->fetch(); - } - - - - /** - * Adds select clause, more calls appends to the end. - * @param string for example "column, MD5(column) AS column_md5" - * @return Selection provides a fluent interface - */ - public function select($columns) - { - $this->__destruct(); - $this->select[] = $this->tryDelimite($columns); - return $this; - } - - - - /** - * Selects by primary key. - * @param mixed - * @return Selection provides a fluent interface - */ - public function find($key) - { - return $this->where($this->delimitedPrimary, $key); - } - - - - /** - * Adds where condition, more calls appends with AND. - * @param string condition possibly containing ? - * @param mixed - * @param mixed ... - * @return Selection provides a fluent interface - */ - public function where($condition, $parameters = array()) - { - if (is_array($condition)) { // where(array('column1' => 1, 'column2 > ?' => 2)) - foreach ($condition as $key => $val) { - $this->where($key, $val); - } - return $this; - } - - $this->__destruct(); - - $this->conditions[] = $condition = $this->tryDelimite($condition); - - $args = func_num_args(); - if ($args !== 2 || strpbrk($condition, '?:')) { // where('column < ? OR column > ?', array(1, 2)) - if ($args !== 2 || !is_array($parameters)) { // where('column < ? OR column > ?', 1, 2) - $parameters = func_get_args(); - array_shift($parameters); - } - $this->parameters = array_merge($this->parameters, $parameters); - - } elseif ($parameters === NULL) { // where('column', NULL) - $condition .= ' IS NULL'; - - } elseif ($parameters instanceof Selection) { // where('column', $db->$table()) - $clone = clone $parameters; - if (!$clone->select) { - $clone->select = array($this->getPrimary($clone->name)); - } - if ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql') { - $condition .= " IN ($clone)"; - } else { - $in = array(); - foreach ($clone as $row) { - $this->parameters[] = array_values(iterator_to_array($row)); - $in[] = (count($row) === 1 ? '?' : '(?)'); - } - $condition .= ' IN (' . ($in ? implode(', ', $in) : 'NULL') . ')'; - } - - } elseif (!is_array($parameters)) { // where('column', 'x') - $condition .= ' = ?'; - $this->parameters[] = $parameters; - - } else { // where('column', array(1, 2)) - if ($parameters) { - $condition .= " IN (?)"; - $this->parameters[] = $parameters; - } else { - $condition .= " IN (NULL)"; - } - } - - $this->where[] = $condition; - return $this; - } - - - - /** - * Adds order clause, more calls appends to the end. - * @param string for example 'column1, column2 DESC' - * @return Selection provides a fluent interface - */ - public function order($columns) - { - $this->rows = NULL; - $this->order[] = $this->tryDelimite($columns); - return $this; - } - - - - /** - * Sets limit clause, more calls rewrite old values. - * @param int - * @param int - * @return Selection provides a fluent interface - */ - public function limit($limit, $offset = NULL) - { - $this->rows = NULL; - $this->limit = $limit; - $this->offset = $offset; - return $this; - } - - - - /** - * Sets group clause, more calls rewrite old values. - * @param string - * @param string - * @return Selection provides a fluent interface - */ - public function group($columns, $having = '') - { - $this->__destruct(); - $this->group = $this->tryDelimite($columns); - $this->having = $having; - return $this; - } - - - - /** - * Executes aggregation function. - * @param string - * @return string - */ - public function aggregation($function) - { - $join = $this->createJoins(implode(',', $this->conditions), TRUE) + $this->createJoins($function); - $query = "SELECT $function FROM $this->delimitedName" . implode($join); - if ($this->where) { - $query .= ' WHERE (' . implode(') AND (', $this->where) . ')'; - } - foreach ($this->query($query)->fetch() as $val) { - return $val; - } - } - - - - /** - * Counts number of rows. - * @param string - * @return int - */ - public function count($column = '') - { - if (!$column) { - $this->execute(); - return count($this->data); - } - return $this->aggregation("COUNT({$this->tryDelimite($column)})"); - } - - - - /** - * Returns minimum value from a column. - * @param string - * @return int - */ - public function min($column) - { - return $this->aggregation("MIN({$this->tryDelimite($column)})"); - } - - - - /** - * Returns maximum value from a column. - * @param string - * @return int - */ - public function max($column) - { - return $this->aggregation("MAX({$this->tryDelimite($column)})"); - } - - - - /** - * Returns sum of values in a column. - * @param string - * @return int - */ - public function sum($column) - { - return $this->aggregation("SUM({$this->tryDelimite($column)})"); - } - - - - /** - * Returns SQL query. - * @return string - */ - public function getSql() - { - $join = $this->createJoins(implode(',', $this->conditions), TRUE) - + $this->createJoins(implode(',', $this->select) . ",$this->group,$this->having," . implode(',', $this->order)); - - if ($this->rows === NULL && $this->connection->cache && !is_string($this->prevAccessed)) { - $this->accessed = $this->prevAccessed = $this->connection->cache->load(array(__CLASS__, $this->name, $this->conditions)); - } - - $prefix = $join ? "$this->delimitedName." : ''; - if ($this->select) { - $cols = implode(', ', $this->select); - - } elseif ($this->prevAccessed) { - $cols = $prefix . implode(', ' . $prefix, array_map(array($this->connection->getSupplementalDriver(), 'delimite'), array_keys($this->prevAccessed))); - - } else { - $cols = $prefix . '*'; - } - - return "SELECT{$this->topString()} $cols FROM $this->delimitedName" . implode($join) . $this->whereString(); - } - - - - protected function createJoins($val, $inner = FALSE) - { - $supplementalDriver = $this->connection->getSupplementalDriver(); - $joins = array(); - preg_match_all('~\\b(\\w+)\\.(\\w+)(\\s+IS\\b|\\s*<=>)?~i', $val, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $name = $match[1]; - if ($name !== $this->name) { // case-sensitive - $table = $this->connection->databaseReflection->getReferencedTable($name, $this->name); - $column = $this->connection->databaseReflection->getReferencedColumn($name, $this->name); - $primary = $this->getPrimary($table); - $joins[$name] = ' ' . (!isset($joins[$name]) && $inner && !isset($match[3]) ? 'INNER' : 'LEFT') - . ' JOIN ' . $supplementalDriver->delimite($table) - . ($table !== $name ? ' AS ' . $supplementalDriver->delimite($name) : '') - . " ON $this->delimitedName." . $supplementalDriver->delimite($column) - . ' = ' . $supplementalDriver->delimite($name) . '.' . $supplementalDriver->delimite($primary); - } - } - return $joins; - } - - - - /** - * Executes built query. - * @return NULL - */ - protected function execute() - { - if ($this->rows !== NULL) { - return; - } - - try { - $result = $this->query($this->getSql()); - - } catch (\PDOException $exception) { - if (!$this->select && $this->prevAccessed) { - $this->prevAccessed = ''; - $this->accessed = array(); - $result = $this->query($this->getSql()); - } else { - throw $exception; - } - } - - $this->rows = array(); - $result->setFetchMode(PDO::FETCH_ASSOC); - foreach ($result as $key => $row) { - $row = $result->normalizeRow($row); - $this->rows[isset($row[$this->primary]) ? $row[$this->primary] : $key] = new ActiveRow($row, $this); - } - $this->data = $this->rows; - - if (isset($row[$this->primary]) && !is_string($this->accessed)) { - $this->accessed[$this->primary] = TRUE; - } - } - - - - protected function whereString() - { - $return = ''; - $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME); - $where = $this->where; - if ($this->limit !== NULL && $driver === 'oci') { - $where[] = ($this->offset ? "rownum > $this->offset AND " : '') . 'rownum <= ' . ($this->limit + $this->offset); - } - if ($where) { - $return .= ' WHERE (' . implode(') AND (', $where) . ')'; - } - if ($this->group) { - $return .= " GROUP BY $this->group"; - } - if ($this->having) { - $return .= " HAVING $this->having"; - } - if ($this->order) { - $return .= ' ORDER BY ' . implode(', ', $this->order); - } - if ($this->limit !== NULL && $driver !== 'oci' && $driver !== 'dblib') { - $return .= " LIMIT $this->limit"; - if ($this->offset !== NULL) { - $return .= " OFFSET $this->offset"; - } - } - return $return; - } - - - - protected function topString() - { - if ($this->limit !== NULL && $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) === 'dblib') { - return " TOP ($this->limit)"; //! offset is not supported - } - return ''; - } - - - - protected function tryDelimite($s) - { - return preg_match('#^[a-z_][a-z0-9_.]*$#i', $s) // is identifier? - ? implode('.', array_map(array($this->connection->getSupplementalDriver(), 'delimite'), explode('.', $s))) - : $s; - } - - - - protected function query($query) - { - return $this->connection->queryArgs($query, $this->parameters); - } - - - - public function access($key, $delete = FALSE) - { - if ($delete) { - if (is_array($this->accessed)) { - $this->accessed[$key] = FALSE; - } - return FALSE; - } - - if ($key === NULL) { - $this->accessed = ''; - - } elseif (!is_string($this->accessed)) { - $this->accessed[$key] = TRUE; - } - - if (!$this->select && $this->prevAccessed && ($key === NULL || !isset($this->prevAccessed[$key]))) { - $this->prevAccessed = ''; - $this->rows = NULL; - return TRUE; - } - return FALSE; - } - - - - /********************* manipulation ****************d*g**/ - - - - /** - * Inserts row in a table. - * @param mixed array($column => $value)|Traversable for single row insert or TableSelection|string for INSERT ... SELECT - * @return ActiveRow or FALSE in case of an error or number of affected rows for INSERT ... SELECT - */ - public function insert($data) - { - if ($data instanceof Selection) { - $data = $data->getSql(); - - } elseif ($data instanceof \Traversable) { - $data = iterator_to_array($data); - } - - $return = $this->connection->query("INSERT INTO $this->delimitedName", $data); - - $this->rows = NULL; - if (!is_array($data)) { - return $return->rowCount(); - } - - if (!isset($data[$this->primary]) && ($id = $this->connection->lastInsertId())) { - $data[$this->primary] = $id; - } - return new ActiveRow($data, $this); - } - - - - /** - * Updates all rows in result set. - * @param array ($column => $value) - * @return int number of affected rows or FALSE in case of an error - */ - public function update($data) - { - if ($data instanceof \Traversable) { - $data = iterator_to_array($data); - - } elseif (!is_array($data)) { - throw new Nette\InvalidArgumentException; - } - - if (!$data) { - return 0; - } - // joins in UPDATE are supported only in MySQL - return $this->connection->queryArgs( - 'UPDATE' . $this->topString() . " $this->delimitedName SET ?" . $this->whereString(), - array_merge(array($data), $this->parameters) - )->rowCount(); - } - - - - /** - * Deletes all rows in result set. - * @return int number of affected rows or FALSE in case of an error - */ - public function delete() - { - return $this->query( - 'DELETE' . $this->topString() . " FROM $this->delimitedName" . $this->whereString() - )->rowCount(); - } - - - - /********************* references ****************d*g**/ - - - - /** - * Returns referenced row. - * @param string - * @return ActiveRow or NULL if the row does not exist - */ - public function getReferencedTable($name, & $column = NULL) - { - $column = $this->connection->databaseReflection->getReferencedColumn($name, $this->name); - $referenced = & $this->referenced[$name]; - if ($referenced === NULL) { - $keys = array(); - foreach ($this->rows as $row) { - if ($row[$column] !== NULL) { - $keys[$row[$column]] = NULL; - } - } - if ($keys) { - $table = $this->connection->databaseReflection->getReferencedTable($name, $this->name); - $referenced = new Selection($table, $this->connection); - $referenced->where($table . '.' . $this->getPrimary($table), array_keys($keys)); - } else { - $referenced = array(); - } - } - return $referenced; - } - - - - /** - * Returns referencing rows. - * @param string table name - * @return GroupedSelection - */ - public function getReferencingTable($table) - { - $column = $this->connection->databaseReflection->getReferencingColumn($table, $this->name); - $referencing = new GroupedSelection($table, $this, $column); - $referencing->where("$table.$column", array_keys((array) $this->rows)); // (array) - is NULL after insert - return $referencing; - } - - - - private function getPrimary($table) - { - return $this->connection->databaseReflection->getPrimary($table); - } - - - - /********************* interface Iterator ****************d*g**/ - - - - public function rewind() - { - $this->execute(); - $this->keys = array_keys($this->data); - reset($this->keys); - } - - - - /** @return ActiveRow */ - public function current() - { - return $this->data[current($this->keys)]; - } - - - - /** - * @return string row ID - */ - public function key() - { - return current($this->keys); - } - - - - public function next() - { - next($this->keys); - } - - - - public function valid() - { - return current($this->keys) !== FALSE; - } - - - - /********************* interface ArrayAccess ****************d*g**/ - - - - /** - * Mimic row. - * @param string row ID - * @param ActiveRow - * @return NULL - */ - public function offsetSet($key, $value) - { - $this->execute(); - $this->data[$key] = $value; - } - - - - /** - * Returns specified row. - * @param string row ID - * @return ActiveRow or NULL if there is no such row - */ - public function offsetGet($key) - { - $this->execute(); - return $this->data[$key]; - } - - - - /** - * Tests if row exists. - * @param string row ID - * @return bool - */ - public function offsetExists($key) - { - $this->execute(); - return isset($this->data[$key]); - } - - - - /** - * Removes row from result set. - * @param string row ID - * @return NULL - */ - public function offsetUnset($key) - { - $this->execute(); - unset($this->data[$key]); - } - - - - /** - * Returns next row of result. - * @return ActiveRow or FALSE if there is no row - */ - public function fetch() - { - $this->execute(); - $return = current($this->data); - next($this->data); - return $return; - } - - - - /** - * Returns all rows as associative array. - * @param string - * @param string column name used for an array value or an empty string for the whole row - * @return array - */ - public function fetchPairs($key, $value = '') - { - $return = array(); - // no $clone->select = array($key, $value) to allow efficient caching with repetitive calls with different parameters - foreach ($this as $row) { - $return[$row[$key]] = ($value !== '' ? $row[$value] : $row); - } - return $return; - } - -} + ActiveRow] format */ + protected $rows; + + /** @var ActiveRow[] modifiable data in [primary key => ActiveRow] format */ + protected $data; + + /** @var Selection[] */ + protected $referenced = array(); + + /** @var array of [sqlQuery-hash => grouped data]; used by GroupedSelection */ + protected $referencing = array(); + + /** @var GroupedSelection[] cached array of GroupedSelection prototypes */ + protected $referencingPrototype = array(); + + /** @var array of [conditions => [key => ActiveRow]]; used by GroupedSelection */ + protected $aggregation = array(); + + /** @var array of touched columns */ + protected $accessed; + + /** @var array of earlier touched columns */ + protected $prevAccessed; + + /** @var bool should instance observe accessed columns caching */ + protected $observeCache = FALSE; + + /** @var bool recheck referencing keys */ + protected $checkReferenced = FALSE; + + /** @var array of primary key values */ + protected $keys = array(); + + + + /** + * Creates filtered table representation. + * @param string database table name + * @param Nette\Database\Connection + */ + public function __construct($table, Nette\Database\Connection $connection) + { + $this->name = $table; + $this->connection = $connection; + $this->primary = $connection->getDatabaseReflection()->getPrimary($table); + $this->sqlBuilder = new SqlBuilder($this); + } + + + + public function __destruct() + { + $this->saveCacheState(); + } + + + + public function __clone() + { + $this->sqlBuilder = clone $this->sqlBuilder; + $this->sqlBuilder->setSelection($this); + } + + + + /** + * @return Nette\Database\Connection + */ + public function getConnection() + { + return $this->connection; + } + + + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + + + /** + * @return string + */ + public function getPrimary() + { + return $this->primary; + } + + + + /** + * @return string + */ + public function getPrimarySequence() + { + if ($this->primarySequence === FALSE) { + $this->primarySequence = NULL; + + $driver = $this->connection->getSupplementalDriver(); + if ($driver->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE)) { + foreach ($driver->getColumns($this->name) as $column) { + if ($column['name'] === $this->primary) { + $this->primarySequence = $column['vendor']['sequence']; + break; + } + } + } + } + + return $this->primarySequence; + } + + + + /** + * @param string + * @return Selection provides a fluent interface + */ + public function setPrimarySequence($sequence) + { + $this->primarySequence = $sequence; + return $this; + } + + + + /** + * @return string + */ + public function getSql() + { + return $this->sqlBuilder->buildSelectQuery(); + } + + + + /** + * Loads cache of previous accessed columns and returns it. + * @internal + * @return array|false + */ + public function getPreviousAccessed() + { + $cache = $this->connection->getCache(); + if ($this->rows === NULL && $cache && !is_string($this->prevAccessed)) { + $this->accessed = $this->prevAccessed = $cache->load(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions())); + } + + return $this->prevAccessed; + } + + + + /** + * @internal + * @return SqlBuilder + */ + public function getSqlBuilder() + { + return $this->sqlBuilder; + } + + + + /********************* quick access ****************d*g**/ + + + + /** + * Returns row specified by primary key. + * @param mixed primary key + * @return ActiveRow or FALSE if there is no such row + */ + public function get($key) + { + $clone = clone $this; + $clone->where($this->primary, $key); + return $clone->fetch(); + } + + + + /** + * Returns next row of result. + * @return ActiveRow or FALSE if there is no row + */ + public function fetch() + { + $this->execute(); + $return = current($this->data); + next($this->data); + return $return; + } + + + + /** + * Returns all rows as associative array. + * @param string + * @param string column name used for an array value or NULL for the whole row + * @return array + */ + public function fetchPairs($key, $value = NULL) + { + $return = array(); + foreach ($this as $row) { + $return[is_object($row[$key]) ? (string) $row[$key] : $row[$key]] = ($value ? $row[$value] : $row); + } + return $return; + } + + + + /********************* sql selectors ****************d*g**/ + + + + /** + * Adds select clause, more calls appends to the end. + * @param string for example "column, MD5(column) AS column_md5" + * @return Selection provides a fluent interface + */ + public function select($columns) + { + $this->emptyResultSet(); + $this->sqlBuilder->addSelect($columns); + return $this; + } + + + + /** + * Selects by primary key. + * @param mixed + * @return Selection provides a fluent interface + */ + public function find($key) + { + return $this->where($this->primary, $key); + } + + + + /** + * Adds where condition, more calls appends with AND. + * @param string condition possibly containing ? + * @param mixed + * @param mixed ... + * @return Selection provides a fluent interface + */ + public function where($condition, $parameters = array()) + { + if (is_array($condition)) { // where(array('column1' => 1, 'column2 > ?' => 2)) + foreach ($condition as $key => $val) { + if (is_int($key)) { + $this->where($val); // where('full condition') + } else { + $this->where($key, $val); // where('column', 1) + } + } + return $this; + } + + $args = func_get_args(); + if (call_user_func_array(array($this->sqlBuilder, 'addWhere'), $args)) { + $this->emptyResultSet(); + } + + return $this; + } + + + + /** + * Adds order clause, more calls appends to the end. + * @param string for example 'column1, column2 DESC' + * @return Selection provides a fluent interface + */ + public function order($columns) + { + $this->emptyResultSet(); + $this->sqlBuilder->addOrder($columns); + return $this; + } + + + + /** + * Sets limit clause, more calls rewrite old values. + * @param int + * @param int + * @return Selection provides a fluent interface + */ + public function limit($limit, $offset = NULL) + { + $this->emptyResultSet(); + $this->sqlBuilder->setLimit($limit, $offset); + return $this; + } + + + + /** + * Sets offset using page number, more calls rewrite old values. + * @param int + * @param int + * @return Selection provides a fluent interface + */ + public function page($page, $itemsPerPage) + { + return $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage); + } + + + + /** + * Sets group clause, more calls rewrite old values. + * @param string + * @param string + * @return Selection provides a fluent interface + */ + public function group($columns, $having = NULL) + { + $this->emptyResultSet(); + $this->sqlBuilder->setGroup($columns, $having); + return $this; + } + + + + /********************* aggregations ****************d*g**/ + + + + /** + * Executes aggregation function. + * @param string select call in "FUNCTION(column)" format + * @return string + */ + public function aggregation($function) + { + $selection = $this->createSelectionInstance(); + $selection->getSqlBuilder()->importConditions($this->getSqlBuilder()); + $selection->select($function); + foreach ($selection->fetch() as $val) { + return $val; + } + } + + + + /** + * Counts number of rows. + * @param string if it is not provided returns count of result rows, otherwise runs new sql counting query + * @return int + */ + public function count($column = NULL) + { + if (!$column) { + $this->execute(); + return count($this->data); + } + return $this->aggregation("COUNT($column)"); + } + + + + /** + * Returns minimum value from a column. + * @param string + * @return int + */ + public function min($column) + { + return $this->aggregation("MIN($column)"); + } + + + + /** + * Returns maximum value from a column. + * @param string + * @return int + */ + public function max($column) + { + return $this->aggregation("MAX($column)"); + } + + + + /** + * Returns sum of values in a column. + * @param string + * @return int + */ + public function sum($column) + { + return $this->aggregation("SUM($column)"); + } + + + + /********************* internal ****************d*g**/ + + + + protected function execute() + { + if ($this->rows !== NULL) { + return; + } + + $this->observeCache = TRUE; + + try { + $result = $this->query($this->sqlBuilder->buildSelectQuery()); + + } catch (\PDOException $exception) { + if (!$this->sqlBuilder->getSelect() && $this->prevAccessed) { + $this->prevAccessed = ''; + $this->accessed = array(); + $result = $this->query($this->sqlBuilder->buildSelectQuery()); + } else { + throw $exception; + } + } + + $this->rows = array(); + $result->setFetchMode(PDO::FETCH_ASSOC); + foreach ($result as $key => $row) { + $row = $result->normalizeRow($row); + $this->rows[isset($row[$this->primary]) ? $row[$this->primary] : $key] = $this->createRow($row); + } + $this->data = $this->rows; + + if (isset($row[$this->primary]) && !is_string($this->accessed)) { + $this->accessed[$this->primary] = TRUE; + } + } + + + + protected function createRow(array $row) + { + return new ActiveRow($row, $this); + } + + + + protected function createSelectionInstance($table = NULL) + { + return new Selection($table ?: $this->name, $this->connection); + } + + + + protected function createGroupedSelectionInstance($table, $column) + { + return new GroupedSelection($this, $table, $column); + } + + + + protected function query($query) + { + return $this->connection->queryArgs($query, $this->sqlBuilder->getParameters()); + } + + + + protected function emptyResultSet() + { + if ($this->rows === NULL) { + return; + } + + $this->rows = NULL; + $this->saveCacheState(); + } + + + + protected function saveCacheState() + { + if ($this->observeCache && ($cache = $this->connection->getCache()) && !$this->sqlBuilder->getSelect() && $this->accessed != $this->prevAccessed) { + $cache->save(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions()), $this->accessed); + } + } + + + + /** + * Returns Selection parent for caching. + * @return Selection + */ + protected function getRefTable(& $refPath) + { + return $this; + } + + + + /** + * @internal + * @param string column name + * @param bool|NULL TRUE - cache, FALSE - don't cache, NULL - remove + * @return bool + */ + public function access($key, $cache = TRUE) + { + if ($cache === NULL) { + if (is_array($this->accessed)) { + $this->accessed[$key] = FALSE; + } + return FALSE; + } + + if ($key === NULL) { + $this->accessed = ''; + + } elseif (!is_string($this->accessed)) { + $this->accessed[$key] = $cache; + } + + if ($cache && !$this->sqlBuilder->getSelect() && $this->prevAccessed && ($key === NULL || !isset($this->prevAccessed[$key]))) { + $this->prevAccessed = ''; + $this->emptyResultSet(); + return TRUE; + } + + return FALSE; + } + + + + /********************* manipulation ****************d*g**/ + + + + /** + * Inserts row in a table. + * @param mixed array($column => $value)|Traversable for single row insert or Selection|string for INSERT ... SELECT + * @return ActiveRow or FALSE in case of an error or number of affected rows for INSERT ... SELECT + */ + public function insert($data) + { + if ($data instanceof Selection) { + $data = $data->getSql(); + + } elseif ($data instanceof \Traversable) { + $data = iterator_to_array($data); + } + + $return = $this->connection->query($this->sqlBuilder->buildInsertQuery(), $data); + $this->checkReferenced = TRUE; + + if (!is_array($data)) { + return $return->rowCount(); + } + + if (!isset($data[$this->primary]) && ($id = $this->connection->lastInsertId($this->getPrimarySequence()))) { + $data[$this->primary] = $id; + return $this->rows[$id] = $this->createRow($data); + + } else { + return $this->createRow($data); + + } + } + + + + /** + * Updates all rows in result set. + * Joins in UPDATE are supported only in MySQL + * @param array|\Traversable ($column => $value) + * @return int number of affected rows or FALSE in case of an error + */ + public function update($data) + { + if ($data instanceof \Traversable) { + $data = iterator_to_array($data); + + } elseif (!is_array($data)) { + throw new Nette\InvalidArgumentException; + } + + if (!$data) { + return 0; + } + + return $this->connection->queryArgs( + $this->sqlBuilder->buildUpdateQuery(), + array_merge(array($data), $this->sqlBuilder->getParameters()) + )->rowCount(); + } + + + + /** + * Deletes all rows in result set. + * @return int number of affected rows or FALSE in case of an error + */ + public function delete() + { + return $this->query($this->sqlBuilder->buildDeleteQuery())->rowCount(); + } + + + + /********************* references ****************d*g**/ + + + + /** + * Returns referenced row. + * @param string + * @param string + * @param bool checks if rows contains the same primary value relations + * @return Selection or array() if the row does not exist + */ + public function getReferencedTable($table, $column, $checkReferenced = FALSE) + { + $referenced = & $this->getRefTable($refPath)->referenced[$refPath . "$table.$column"]; + if ($referenced === NULL || $checkReferenced || $this->checkReferenced) { + $this->execute(); + $this->checkReferenced = FALSE; + $keys = array(); + foreach ($this->rows as $row) { + if ($row[$column] === NULL) + continue; + + $key = $row[$column] instanceof ActiveRow ? $row[$column]->getPrimary() : $row[$column]; + $keys[$key] = TRUE; + } + + if ($referenced !== NULL && array_keys($keys) === array_keys($referenced->rows)) { + return $referenced; + } + + if ($keys) { + $referenced = $this->createSelectionInstance($table); + $referenced->where($referenced->primary, array_keys($keys)); + } else { + $referenced = array(); + } + } + + return $referenced; + } + + + + /** + * Returns referencing rows. + * @param string + * @param string + * @param int primary key + * @return GroupedSelection + */ + public function getReferencingTable($table, $column, $active = NULL) + { + $prototype = & $this->getRefTable($refPath)->referencingPrototype[$refPath . "$table.$column"]; + if (!$prototype) { + $prototype = $this->createGroupedSelectionInstance($table, $column); + $prototype->where("$table.$column", array_keys((array) $this->rows)); + } + + $clone = clone $prototype; + $clone->setActive($active); + return $clone; + } + + + + /********************* interface Iterator ****************d*g**/ + + + + public function rewind() + { + $this->execute(); + $this->keys = array_keys($this->data); + reset($this->keys); + } + + + + /** @return ActiveRow */ + public function current() + { + return $this->data[current($this->keys)]; + } + + + + /** + * @return string row ID + */ + public function key() + { + return current($this->keys); + } + + + + public function next() + { + next($this->keys); + } + + + + public function valid() + { + return current($this->keys) !== FALSE; + } + + + + /********************* interface ArrayAccess ****************d*g**/ + + + + /** + * Mimic row. + * @param string row ID + * @param ActiveRow + * @return NULL + */ + public function offsetSet($key, $value) + { + $this->execute(); + $this->data[$key] = $value; + } + + + + /** + * Returns specified row. + * @param string row ID + * @return ActiveRow or NULL if there is no such row + */ + public function offsetGet($key) + { + $this->execute(); + return $this->data[$key]; + } + + + + /** + * Tests if row exists. + * @param string row ID + * @return bool + */ + public function offsetExists($key) + { + $this->execute(); + return isset($this->data[$key]); + } + + + + /** + * Removes row from result set. + * @param string row ID + * @return NULL + */ + public function offsetUnset($key) + { + $this->execute(); + unset($this->data[$key]); + } + +} diff --git a/libs/Nette/Database/Table/SqlBuilder.php b/libs/Nette/Database/Table/SqlBuilder.php new file mode 100644 index 0000000..2e7e104 --- /dev/null +++ b/libs/Nette/Database/Table/SqlBuilder.php @@ -0,0 +1,388 @@ +selection = $selection; + $this->connection = $selection->getConnection(); + $this->delimitedTable = $this->tryDelimite($selection->getName()); + } + + + + public function setSelection(Selection $selection) + { + $this->selection = $selection; + } + + + + public function buildInsertQuery() + { + return "INSERT INTO {$this->delimitedTable}"; + } + + + + public function buildUpdateQuery() + { + return "UPDATE{$this->buildTopClause()} {$this->delimitedTable} SET ?" . $this->buildConditions(); + } + + + + public function buildDeleteQuery() + { + return "DELETE{$this->buildTopClause()} FROM {$this->delimitedTable}" . $this->buildConditions(); + } + + + + public function importConditions(SqlBuilder $builder) + { + $this->where = $builder->where; + $this->parameters = $builder->parameters; + $this->conditions = $builder->conditions; + } + + + + /********************* SQL selectors ****************d*g**/ + + + + public function addSelect($columns) + { + $this->select[] = $columns; + } + + + + public function getSelect() + { + return $this->select; + } + + + + public function addWhere($condition, $parameters = array()) + { + $args = func_get_args(); + $hash = md5(json_encode($args)); + if (isset($this->conditions[$hash])) { + return FALSE; + } + + $this->conditions[$hash] = $condition; + $condition = $this->removeExtraTables($condition); + $condition = $this->tryDelimite($condition); + + if (count($args) !== 2 || strpbrk($condition, '?:')) { // where('column < ? OR column > ?', array(1, 2)) + if (count($args) !== 2 || !is_array($parameters)) { // where('column < ? OR column > ?', 1, 2) + $parameters = $args; + array_shift($parameters); + } + + $this->parameters = array_merge($this->parameters, $parameters); + + } elseif ($parameters === NULL) { // where('column', NULL) + $condition .= ' IS NULL'; + + } elseif ($parameters instanceof Selection) { // where('column', $db->$table()) + $clone = clone $parameters; + if (!$clone->getSqlBuilder()->select) { + $clone->select($clone->primary); + } + + if ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql') { + $condition .= ' IN (' . $clone->getSql() . ')'; + } else { + $in = array(); + foreach ($clone as $row) { + $this->parameters[] = array_values(iterator_to_array($row)); + $in[] = (count($row) === 1 ? '?' : '(?)'); + } + $condition .= ' IN (' . ($in ? implode(', ', $in) : 'NULL') . ')'; + } + + } elseif (!is_array($parameters)) { // where('column', 'x') + $condition .= ' = ?'; + $this->parameters[] = $parameters; + + } else { // where('column', array(1, 2)) + if ($parameters) { + $condition .= " IN (?)"; + $this->parameters[] = $parameters; + } else { + $condition .= " IN (NULL)"; + } + } + + $this->where[] = $condition; + return TRUE; + } + + + + public function getConditions() + { + return array_values($this->conditions); + } + + + + public function addOrder($columns) + { + $this->order[] = $columns; + } + + + + public function getOrder() + { + return $this->order; + } + + + + public function setLimit($limit, $offset) + { + $this->limit = $limit; + $this->offset = $offset; + } + + + + public function getLimit() + { + return $this->limit; + } + + + + public function getOffset() + { + return $this->offset; + } + + + + public function setGroup($columns, $having) + { + $this->group = $columns; + $this->having = $having; + } + + + + public function getGroup() + { + return $this->group; + } + + + + public function getHaving() + { + return $this->having; + } + + + + /********************* SQL building ****************d*g**/ + + + + /** + * Returns SQL query. + * @return string + */ + public function buildSelectQuery() + { + $join = $this->buildJoins(implode(',', $this->conditions), TRUE); + $join += $this->buildJoins(implode(',', $this->select) . ",{$this->group},{$this->having}," . implode(',', $this->order)); + + $prefix = $join ? "{$this->delimitedTable}." : ''; + if ($this->select) { + $cols = $this->tryDelimite($this->removeExtraTables(implode(', ', $this->select))); + + } elseif ($prevAccessed = $this->selection->getPreviousAccessed()) { + $cols = array_map(array($this->connection->getSupplementalDriver(), 'delimite'), array_keys(array_filter($prevAccessed))); + $cols = $prefix . implode(', ' . $prefix, $cols); + + } elseif ($this->group && !$this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_SELECT_UNGROUPED_COLUMNS)) { + $cols = $this->tryDelimite($this->removeExtraTables($this->group)); + + } else { + $cols = $prefix . '*'; + + } + + return "SELECT{$this->buildTopClause()} {$cols} FROM {$this->delimitedTable}" . implode($join) . $this->buildConditions(); + } + + + + public function getParameters() + { + return $this->parameters; + } + + + + protected function buildJoins($val, $inner = FALSE) + { + $driver = $this->selection->getConnection()->getSupplementalDriver(); + $reflection = $this->selection->getConnection()->getDatabaseReflection(); + $joins = array(); + preg_match_all('~\\b([a-z][\\w.:]*[.:])([a-z]\\w*|\*)(\\s+IS\\b|\\s*<=>)?~i', $val, $matches); + foreach ($matches[1] as $names) { + $parent = $this->selection->getName(); + if ($names !== "$parent.") { // case-sensitive + preg_match_all('~\\b([a-z][\\w]*|\*)([.:])~i', $names, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + list(, $name, $delimiter) = $match; + + if ($delimiter === ':') { + list($table, $primary) = $reflection->getHasManyReference($parent, $name); + $column = $reflection->getPrimary($parent); + } else { + list($table, $column) = $reflection->getBelongsToReference($parent, $name); + $primary = $reflection->getPrimary($table); + } + + $joins[$name] = ' ' + . (!isset($joins[$name]) && $inner && !isset($match[3]) ? 'INNER' : 'LEFT') + . ' JOIN ' . $driver->delimite($table) . ($table !== $name ? ' AS ' . $driver->delimite($name) : '') + . ' ON ' . $driver->delimite($parent) . '.' . $driver->delimite($column) + . ' = ' . $driver->delimite($name) . '.' . $driver->delimite($primary); + + $parent = $name; + } + } + } + return $joins; + } + + + + protected function buildConditions() + { + $return = ''; + $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME); + $where = $this->where; + if ($this->limit !== NULL && $driver === 'oci') { + $where[] = ($this->offset ? "rownum > $this->offset AND " : '') . 'rownum <= ' . ($this->limit + $this->offset); + } + if ($where) { + $return .= ' WHERE (' . implode(') AND (', $where) . ')'; + } + if ($this->group) { + $return .= ' GROUP BY '. $this->tryDelimite($this->removeExtraTables($this->group)); + } + if ($this->having) { + $return .= ' HAVING '. $this->tryDelimite($this->removeExtraTables($this->having)); + } + if ($this->order) { + $return .= ' ORDER BY ' . $this->tryDelimite($this->removeExtraTables(implode(', ', $this->order))); + } + if ($this->limit !== NULL && $driver !== 'oci' && $driver !== 'dblib') { + $return .= " LIMIT $this->limit"; + if ($this->offset !== NULL) { + $return .= " OFFSET $this->offset"; + } + } + return $return; + } + + + + protected function buildTopClause() + { + if ($this->limit !== NULL && $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) === 'dblib') { + return " TOP ($this->limit)"; //! offset is not supported + } + return ''; + } + + + + protected function tryDelimite($s) + { + $driver = $this->connection->getSupplementalDriver(); + return preg_replace_callback('#(?<=[^\w`"\[]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#i', function($m) use ($driver) { + return strtoupper($m[0]) === $m[0] ? $m[0] : $driver->delimite($m[0]); + }, $s); + } + + + + protected function removeExtraTables($expression) + { + return preg_replace('~(?:\\b[a-z_][a-z0-9_.:]*[.:])?([a-z_][a-z0-9_]*)[.:]([a-z_*])~i', '\\1.\\2', $expression); // rewrite tab1.tab2.col + } + +} diff --git a/libs/Nette/Diagnostics/Bar.php b/libs/Nette/Diagnostics/Bar.php index cd40df1..cfe2d68 100644 --- a/libs/Nette/Diagnostics/Bar.php +++ b/libs/Nette/Diagnostics/Bar.php @@ -1,75 +1,79 @@ -panels[$id])); - } - $this->panels[$id] = $panel; - } - - - - /** - * Renders debug bar. - * @return void - */ - public function render() - { - $panels = array(); - foreach ($this->panels as $id => $panel) { - try { - $panels[] = array( - 'id' => preg_replace('#[^a-z0-9]+#i', '-', $id), - 'tab' => $tab = (string) $panel->getTab(), - 'panel' => $tab ? (string) $panel->getPanel() : NULL, - ); - } catch (\Exception $e) { - $panels[] = array( - 'id' => "error-$id", - 'tab' => "Error: $id", - 'panel' => nl2br(htmlSpecialChars((string) $e)), - ); - } - } - require __DIR__ . '/templates/bar.phtml'; - } - -} +panels[$id])); + } + $this->panels[$id] = $panel; + return $this; + } + + + + /** + * Renders debug bar. + * @return void + */ + public function render() + { + $obLevel = ob_get_level(); + $panels = array(); + foreach ($this->panels as $id => $panel) { + try { + $panels[] = array( + 'id' => preg_replace('#[^a-z0-9]+#i', '-', $id), + 'tab' => $tab = (string) $panel->getTab(), + 'panel' => $tab ? (string) $panel->getPanel() : NULL, + ); + } catch (\Exception $e) { + $panels[] = array( + 'id' => "error-" . preg_replace('#[^a-z0-9]+#i', '-', $id), + 'tab' => "Error in $id", + 'panel' => '

    Error: ' . $id . '

    ' . nl2br(htmlSpecialChars($e)) . '
    ', + ); + while (ob_get_level() > $obLevel) { // restore ob-level if broken + ob_end_clean(); + } + } + } + require __DIR__ . '/templates/bar.phtml'; + } + +} diff --git a/libs/Nette/Diagnostics/BlueScreen.php b/libs/Nette/Diagnostics/BlueScreen.php index 06f1367..f9ce51e 100644 --- a/libs/Nette/Diagnostics/BlueScreen.php +++ b/libs/Nette/Diagnostics/BlueScreen.php @@ -1,122 +1,139 @@ -panels[] = $panel; - } else { - $this->panels[$id] = $panel; - } - } - - - - /** - * Renders blue screen. - * @param \Exception - * @return void - */ - public function render(\Exception $exception) - { - $panels = $this->panels; - require __DIR__ . '/templates/bluescreen.phtml'; - } - - - - /** - * Returns syntax highlighted source code. - * @param string - * @param int - * @param int - * @return string - */ - public static function highlightFile($file, $line, $count = 15) - { - if (function_exists('ini_set')) { - ini_set('highlight.comment', '#999; font-style: italic'); - ini_set('highlight.default', '#000'); - ini_set('highlight.html', '#06B'); - ini_set('highlight.keyword', '#D24; font-weight: bold'); - ini_set('highlight.string', '#080'); - } - - $start = max(1, $line - floor($count / 2)); - - $source = @file_get_contents($file); // intentionally @ - if (!$source) { - return; - } - $source = explode("\n", highlight_string($source, TRUE)); - $spans = 1; - $out = $source[0]; // - $source = explode('
    ', $source[1]); - array_unshift($source, NULL); - - $i = $start; // find last highlighted block - while (--$i >= 1) { - if (preg_match('#.*(]*>)#', $source[$i], $m)) { - if ($m[1] !== '
    ') { - $spans++; $out .= $m[1]; - } - break; - } - } - - $source = array_slice($source, $start, $count, TRUE); - end($source); - $numWidth = strlen((string) key($source)); - - foreach ($source as $n => $s) { - $spans += substr_count($s, ']+>#', $s, $tags); - if ($n === $line) { - $out .= sprintf( - "%{$numWidth}s: %s\n%s", - $n, - strip_tags($s), - implode('', $tags[0]) - ); - } else { - $out .= sprintf("%{$numWidth}s: %s\n", $n, $s); - } - } - return $out . str_repeat('', $spans) . '
    '; - } - -} +panels, TRUE)) { + $this->panels[] = $panel; + } + return $this; + } + + + + /** + * Renders blue screen. + * @param \Exception + * @return void + */ + public function render(\Exception $exception) + { + $panels = $this->panels; + require __DIR__ . '/templates/bluescreen.phtml'; + } + + + + /** + * Returns syntax highlighted source code. + * @param string + * @param int + * @param int + * @return string + */ + public static function highlightFile($file, $line, $lines = 15, $vars = array()) + { + $source = @file_get_contents($file); // intentionally @ + if ($source) { + return static::highlightPhp($source, $line, $lines, $vars); + } + } + + + + /** + * Returns syntax highlighted source code. + * @param string + * @param int + * @param int + * @return string + */ + public static function highlightPhp($source, $line, $lines = 15, $vars = array()) + { + if (function_exists('ini_set')) { + ini_set('highlight.comment', '#998; font-style: italic'); + ini_set('highlight.default', '#000'); + ini_set('highlight.html', '#06B'); + ini_set('highlight.keyword', '#D24; font-weight: bold'); + ini_set('highlight.string', '#080'); + } + + $source = str_replace(array("\r\n", "\r"), "\n", $source); + $source = explode("\n", highlight_string($source, TRUE)); + $spans = 1; + $out = $source[0]; // + $source = explode('
    ', $source[1]); + array_unshift($source, NULL); + + $start = $i = max(1, $line - floor($lines * 2/3)); + while (--$i >= 1) { // find last highlighted block + if (preg_match('#.*(]*>)#', $source[$i], $m)) { + if ($m[1] !== '
    ') { + $spans++; $out .= $m[1]; + } + break; + } + } + + $source = array_slice($source, $start, $lines, TRUE); + end($source); + $numWidth = strlen((string) key($source)); + + foreach ($source as $n => $s) { + $spans += substr_count($s, ']+>#', $s, $tags); + if ($n == $line) { + $out .= sprintf( + "%{$numWidth}s: %s\n%s", + $n, + strip_tags($s), + implode('', $tags[0]) + ); + } else { + $out .= sprintf("%{$numWidth}s: %s\n", $n, $s); + } + } + $out .= str_repeat('', $spans) . '
    '; + + $out = preg_replace_callback('#">\$(\w+)( )?#', function($m) use ($vars) { + return isset($vars[$m[1]]) + ? '" title="' . str_replace('"', '"', strip_tags(Helpers::htmlDump($vars[$m[1]]))) . $m[0] + : $m[0]; + }, $out); + + return "
    $out
    "; + } + +} diff --git a/libs/Nette/Diagnostics/Debugger.php b/libs/Nette/Diagnostics/Debugger.php index dd0a4c2..d91c3a3 100644 --- a/libs/Nette/Diagnostics/Debugger.php +++ b/libs/Nette/Diagnostics/Debugger.php @@ -1,663 +1,698 @@ -directory; - self::$email = & self::$logger->email; - self::$mailer = & self::$logger->mailer; - self::$emailSnooze = & Logger::$emailSnooze; - - self::$fireLogger = new FireLogger; - - self::$blueScreen = new BlueScreen; - self::$blueScreen->addPanel(function($e) { - if ($e instanceof Nette\Templating\FilterException) { - return array( - 'tab' => 'Template', - 'panel' => '

    File: ' . Helpers::editorLink($e->sourceFile, $e->sourceLine) - . '  Line: ' . ($e->sourceLine ? $e->sourceLine : 'n/a') . '

    ' - . ($e->sourceLine ? '
    ' . BlueScreen::highlightFile($e->sourceFile, $e->sourceLine) . '
    ' : '') - ); - } - }); - - self::$bar = new Bar; - self::$bar->addPanel(new DefaultBarPanel('time')); - self::$bar->addPanel(new DefaultBarPanel('memory')); - self::$bar->addPanel(self::$errorPanel = new DefaultBarPanel('errors')); // filled by _errorHandler() - self::$bar->addPanel(self::$dumpPanel = new DefaultBarPanel('dumps')); // filled by barDump() - } - - - - /********************* errors and exceptions reporting ****************d*g**/ - - - - /** - * Enables displaying or logging errors and exceptions. - * @param mixed production, development mode, autodetection or IP address(es) whitelist. - * @param string error log directory; enables logging in production mode, FALSE means that logging is disabled - * @param string administrator email; enables email sending in production mode - * @return void - */ - public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL) - { - error_reporting(E_ALL | E_STRICT); - - // production/development mode detection - if (is_bool($mode)) { - self::$productionMode = $mode; - - } elseif (is_string($mode)) { // IP addresses - $mode = preg_split('#[,\s]+#', "$mode 127.0.0.1 ::1"); - } - - if (is_array($mode)) { // IP addresses whitelist detection - self::$productionMode = !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $mode, TRUE); - } - - if (self::$productionMode === self::DETECT) { - if (class_exists('Nette\Environment')) { - self::$productionMode = Nette\Environment::isProduction(); - - } elseif (isset($_SERVER['SERVER_ADDR']) || isset($_SERVER['LOCAL_ADDR'])) { // IP address based detection - $addrs = array(); - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { // proxy server detected - $addrs = preg_split('#,\s*#', $_SERVER['HTTP_X_FORWARDED_FOR']); - } - if (isset($_SERVER['REMOTE_ADDR'])) { - $addrs[] = $_SERVER['REMOTE_ADDR']; - } - $addrs[] = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR']; - self::$productionMode = FALSE; - foreach ($addrs as $addr) { - $oct = explode('.', $addr); - if ($addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31) - && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168'))) - ) { - self::$productionMode = TRUE; - break; - } - } - - } else { - self::$productionMode = !self::$consoleMode; - } - } - - // logging configuration - if (is_string($logDirectory)) { - self::$logDirectory = realpath($logDirectory); - if (self::$logDirectory === FALSE) { - throw new Nette\DirectoryNotFoundException("Directory '$logDirectory' is not found."); - } - } elseif ($logDirectory === FALSE) { - self::$logDirectory = FALSE; - - } else { - self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log' : getcwd() . '/log'; - } - if (self::$logDirectory) { - ini_set('error_log', self::$logDirectory . '/php_error.log'); - } - - // php configuration - if (function_exists('ini_set')) { - ini_set('display_errors', !self::$productionMode); // or 'stderr' - ini_set('html_errors', FALSE); - ini_set('log_errors', FALSE); - - } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally == - throw new Nette\NotSupportedException('Function ini_set() must be enabled.'); - } - - if ($email) { - if (!is_string($email)) { - throw new Nette\InvalidArgumentException('Email address must be a string.'); - } - self::$email = $email; - } - - if (!defined('E_DEPRECATED')) { - define('E_DEPRECATED', 8192); - } - - if (!defined('E_USER_DEPRECATED')) { - define('E_USER_DEPRECATED', 16384); - } - - if (!self::$enabled) { - register_shutdown_function(array(__CLASS__, '_shutdownHandler')); - set_exception_handler(array(__CLASS__, '_exceptionHandler')); - set_error_handler(array(__CLASS__, '_errorHandler')); - self::$enabled = TRUE; - } - } - - - - /** - * Is Debug enabled? - * @return bool - */ - public static function isEnabled() - { - return self::$enabled; - } - - - - /** - * Logs message or exception to file (if not disabled) and sends email notification (if enabled). - * @param string|Exception - * @param int one of constant Debugger::INFO, WARNING, ERROR (sends email), CRITICAL (sends email) - * @return void - */ - public static function log($message, $priority = self::INFO) - { - if (self::$logDirectory === FALSE) { - return; - - } elseif (!self::$logDirectory) { - throw new Nette\InvalidStateException('Logging directory is not specified in Nette\Diagnostics\Debugger::$logDirectory.'); - } - - if ($message instanceof \Exception) { - $exception = $message; - $message = "PHP Fatal error: " - . ($message instanceof Nette\FatalErrorException - ? $exception->getMessage() - : "Uncaught exception " . get_class($exception) . " with message '" . $exception->getMessage() . "'") - . " in " . $exception->getFile() . ":" . $exception->getLine(); - - $hash = md5($exception ); - $exceptionFilename = "exception " . @date('Y-m-d H-i-s') . " $hash.html"; - foreach (new \DirectoryIterator(self::$logDirectory) as $entry) { - if (strpos($entry, $hash)) { - $exceptionFilename = NULL; break; - } - } - } - - self::$logger->log(array( - @date('[Y-m-d H-i-s]'), - $message, - self::$source ? ' @ ' . self::$source : NULL, - !empty($exceptionFilename) ? ' @@ ' . $exceptionFilename : NULL - ), $priority); - - if (!empty($exceptionFilename) && $logHandle = @fopen(self::$logDirectory . '/'. $exceptionFilename, 'w')) { - ob_start(); // double buffer prevents sending HTTP headers in some PHP - ob_start(function($buffer) use ($logHandle) { fwrite($logHandle, $buffer); }, 1); - self::$blueScreen->render($exception); - ob_end_flush(); - ob_end_clean(); - fclose($logHandle); - } - } - - - - /** - * Shutdown handler to catch fatal errors and execute of the planned activities. - * @return void - * @internal - */ - public static function _shutdownHandler() - { - // fatal error handler - static $types = array( - E_ERROR => 1, - E_CORE_ERROR => 1, - E_COMPILE_ERROR => 1, - E_PARSE => 1, - ); - $error = error_get_last(); - if (isset($types[$error['type']])) { - self::_exceptionHandler(new Nette\FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL), TRUE); - } - - // debug bar (require HTML & development mode) - if (self::$bar && !self::$productionMode && !self::$ajaxDetected && !self::$consoleMode - && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())) - ) { - self::$bar->render(); - } - } - - - - /** - * Handler to catch uncaught exception. - * @param \Exception - * @return void - * @internal - */ - public static function _exceptionHandler(\Exception $exception, $drawBar = FALSE) - { - if (!headers_sent()) { // for PHP < 5.2.4 - header('HTTP/1.1 500 Internal Server Error'); - } - - $htmlMode = !self::$ajaxDetected && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())); - - try { - if (self::$productionMode) { - self::log($exception, self::ERROR); - - if (self::$consoleMode) { - echo "ERROR: the server encountered an internal error and was unable to complete your request.\n"; - - } elseif ($htmlMode) { - require __DIR__ . '/templates/error.phtml'; - } - - } else { - if (self::$consoleMode) { // dump to console - echo "$exception\n"; - - } elseif ($htmlMode) { // dump to browser - self::$blueScreen->render($exception); - if ($drawBar && self::$bar) { - self::$bar->render(); - } - - } elseif (!self::fireLog($exception, self::ERROR)) { // AJAX or non-HTML mode - self::log($exception); - } - } - - foreach (self::$onFatalError as $handler) { - call_user_func($handler, $exception); - } - } catch (\Exception $e) { - echo "\nNette\\Debug FATAL ERROR: thrown ", get_class($e), ': ', $e->getMessage(), - "\nwhile processing ", get_class($exception), ': ', $exception->getMessage(), "\n"; - } - exit(255); - } - - - - /** - * Handler to catch warnings and notices. - * @param int level of the error raised - * @param string error message - * @param string file that the error was raised in - * @param int line number the error was raised at - * @param array an array of variables that existed in the scope the error was triggered in - * @return bool FALSE to call normal error handler, NULL otherwise - * @throws Nette\FatalErrorException - * @internal - */ - public static function _errorHandler($severity, $message, $file, $line, $context) - { - if (self::$scream) { - error_reporting(E_ALL | E_STRICT); - } - - if (self::$lastError !== FALSE && ($severity & error_reporting()) === $severity) { // tryError mode - self::$lastError = new \ErrorException($message, 0, $severity, $file, $line); - return NULL; - } - - if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { - throw new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context); - - } elseif (($severity & error_reporting()) !== $severity) { - return FALSE; // calls normal error handler to fill-in error_get_last() - - } elseif (self::$strictMode && !self::$productionMode) { - self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context)); - } - - static $types = array( - E_WARNING => 'Warning', - E_COMPILE_WARNING => 'Warning', // currently unable to handle - E_USER_WARNING => 'Warning', - E_NOTICE => 'Notice', - E_USER_NOTICE => 'Notice', - E_STRICT => 'Strict standards', - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'Deprecated', - ); - - $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message"; - $count = & self::$errorPanel->data["$message|$file|$line"]; - - if ($count++) { // repeated error - return NULL; - - } elseif (self::$productionMode) { - self::log("$message in $file:$line", self::ERROR); - return NULL; - - } else { - $ok = self::fireLog(new \ErrorException($message, 0, $severity, $file, $line), self::WARNING); - return self::$consoleMode || (!self::$bar && !$ok) ? FALSE : NULL; - } - - return FALSE; // call normal error handler - } - - - - /** - * Handles exception thrown in __toString(). - * @param \Exception - * @return void - */ - public static function toStringException(\Exception $exception) - { - if (self::$enabled) { - self::_exceptionHandler($exception); - } else { - trigger_error($exception->getMessage(), E_USER_ERROR); - } - } - - - - /** - * Starts catching potential errors/warnings. - * @return void - */ - public static function tryError() - { - if (!self::$enabled && self::$lastError === FALSE) { - set_error_handler(array(__CLASS__, '_errorHandler')); - } - self::$lastError = NULL; - } - - - - /** - * Returns catched error/warning message. - * @param \ErrorException catched error - * @return bool - */ - public static function catchError(& $error) - { - if (!self::$enabled && self::$lastError !== FALSE) { - restore_error_handler(); - } - $error = self::$lastError; - self::$lastError = FALSE; - return (bool) $error; - } - - - - /********************* useful tools ****************d*g**/ - - - - /** - * Dumps information about a variable in readable format. - * @param mixed variable to dump - * @param bool return output instead of printing it? (bypasses $productionMode) - * @return mixed variable itself or dump - */ - public static function dump($var, $return = FALSE) - { - if (!$return && self::$productionMode) { - return $var; - } - - $output = "
    " . Helpers::htmlDump($var) . "
    \n"; - - if (!$return) { - $trace = debug_backtrace(); - $i = !isset($trace[1]['class']) && isset($trace[1]['function']) && $trace[1]['function'] === 'dump' ? 1 : 0; - if (isset($trace[$i]['file'], $trace[$i]['line']) && is_file($trace[$i]['file'])) { - $lines = file($trace[$i]['file']); - preg_match('#dump\((.*)\)#', $lines[$trace[$i]['line'] - 1], $m); - $output = substr_replace( - $output, - ' title="' . htmlspecialchars((isset($m[0]) ? "$m[0] \n" : '') . "in file {$trace[$i]['file']} on line {$trace[$i]['line']}") . '"', - 4, 0); - - if (self::$showLocation) { - $output = substr_replace( - $output, - ' in ' . Helpers::editorLink($trace[$i]['file'], $trace[$i]['line']) . ":{$trace[$i]['line']}", - -8, 0); - } - } - } - - if (self::$consoleMode) { - $output = htmlspecialchars_decode(strip_tags($output), ENT_NOQUOTES); - } - - if ($return) { - return $output; - - } else { - echo $output; - return $var; - } - } - - - - /** - * Starts/stops stopwatch. - * @param string name - * @return float elapsed seconds - */ - public static function timer($name = NULL) - { - static $time = array(); - $now = microtime(TRUE); - $delta = isset($time[$name]) ? $now - $time[$name] : 0; - $time[$name] = $now; - return $delta; - } - - - - /** - * Dumps information about a variable in Nette Debug Bar. - * @param mixed variable to dump - * @param string optional title - * @return mixed variable itself - */ - public static function barDump($var, $title = NULL) - { - if (!self::$productionMode) { - $dump = array(); - foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) { - $dump[$key] = Helpers::clickableDump($val); - } - self::$dumpPanel->data[] = array('title' => $title, 'dump' => $dump); - } - return $var; - } - - - - /** - * Sends message to FireLogger console. - * @param mixed message to log - * @return bool was successful? - */ - public static function fireLog($message) - { - if (!self::$productionMode) { - return self::$fireLogger->log($message); - } - } - - - - /** @deprecated */ - public static function addPanel(IBarPanel $panel, $id = NULL) - { - self::$bar->addPanel($panel, $id); - } - -} - - - -Debugger::_init(); + '1;33', + 'null' => '1;33', + 'int' => '1;36', + 'float' => '1;36', + 'string' => '1;32', + 'array' => '1;31', + 'key' => '1;37', + 'object' => '1;31', + 'visibility' => '1;30', + 'resource' => '1;37', + ); + + /********************* errors and exceptions reporting ****************d*g**/ + + /** server modes {@link Debugger::enable()} */ + const DEVELOPMENT = FALSE, + PRODUCTION = TRUE, + DETECT = NULL; + + /** @var BlueScreen */ + public static $blueScreen; + + /** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */ + public static $strictMode = FALSE; // $immediateDeath + + /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ + public static $scream = FALSE; + + /** @var array of callables specifies the functions that are automatically called after fatal error */ + public static $onFatalError = array(); + + /** @var bool {@link Debugger::enable()} */ + private static $enabled = FALSE; + + /** @var mixed {@link Debugger::tryError()} FALSE means catching is disabled */ + private static $lastError = FALSE; + + /********************* logging ****************d*g**/ + + /** @var Logger */ + public static $logger; + + /** @var FireLogger */ + public static $fireLogger; + + /** @var string name of the directory where errors should be logged; FALSE means that logging is disabled */ + public static $logDirectory; + + /** @var string email to sent error notifications */ + public static $email; + + /** @deprecated */ + public static $mailer; + + /** @deprecated */ + public static $emailSnooze; + + /********************* debug bar ****************d*g**/ + + /** @var Bar */ + public static $bar; + + /** @var DefaultBarPanel */ + private static $errorPanel; + + /** @var DefaultBarPanel */ + private static $dumpPanel; + + /********************* Firebug extension ****************d*g**/ + + /** {@link Debugger::log()} and {@link Debugger::fireLog()} */ + const DEBUG = 'debug', + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', + CRITICAL = 'critical'; + + + + /** + * Static class - cannot be instantiated. + */ + final public function __construct() + { + throw new Nette\StaticClassException; + } + + + + /** + * Static class constructor. + * @internal + */ + public static function _init() + { + self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE); + self::$consoleMode = PHP_SAPI === 'cli'; + self::$productionMode = self::DETECT; + if (self::$consoleMode) { + self::$source = empty($_SERVER['argv']) ? 'cli' : 'cli: ' . implode(' ', $_SERVER['argv']); + } else { + self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; + if (isset($_SERVER['REQUEST_URI'])) { + self::$source = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') + . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')) + . $_SERVER['REQUEST_URI']; + } + } + + self::$logger = new Logger; + self::$logDirectory = & self::$logger->directory; + self::$email = & self::$logger->email; + self::$mailer = & self::$logger->mailer; + self::$emailSnooze = & Logger::$emailSnooze; + + self::$fireLogger = new FireLogger; + + self::$blueScreen = new BlueScreen; + self::$blueScreen->addPanel(function($e) { + if ($e instanceof Nette\Templating\FilterException) { + return array( + 'tab' => 'Template', + 'panel' => '

    File: ' . Helpers::editorLink($e->sourceFile, $e->sourceLine) + . '  Line: ' . ($e->sourceLine ? $e->sourceLine : 'n/a') . '

    ' + . ($e->sourceLine ? BlueScreen::highlightFile($e->sourceFile, $e->sourceLine) : '') + ); + } elseif ($e instanceof Nette\Utils\NeonException && preg_match('#line (\d+)#', $e->getMessage(), $m)) { + if ($item = Helpers::findTrace($e->getTrace(), 'Nette\Config\Adapters\NeonAdapter::load')) { + return array( + 'tab' => 'NEON', + 'panel' => '

    File: ' . Helpers::editorLink($item['args'][0], $m[1]) . '  Line: ' . $m[1] . '

    ' + . BlueScreen::highlightFile($item['args'][0], $m[1]) + ); + } elseif ($item = Helpers::findTrace($e->getTrace(), 'Nette\Utils\Neon::decode')) { + return array( + 'tab' => 'NEON', + 'panel' => BlueScreen::highlightPhp($item['args'][0], $m[1]) + ); + } + } + }); + + self::$bar = new Bar; + self::$bar->addPanel(new DefaultBarPanel('time')); + self::$bar->addPanel(new DefaultBarPanel('memory')); + self::$bar->addPanel(self::$errorPanel = new DefaultBarPanel('errors')); // filled by _errorHandler() + self::$bar->addPanel(self::$dumpPanel = new DefaultBarPanel('dumps')); // filled by barDump() + } + + + + /********************* errors and exceptions reporting ****************d*g**/ + + + + /** + * Enables displaying or logging errors and exceptions. + * @param mixed production, development mode, autodetection or IP address(es) whitelist. + * @param string error log directory; enables logging in production mode, FALSE means that logging is disabled + * @param string administrator email; enables email sending in production mode + * @return void + */ + public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL) + { + error_reporting(E_ALL | E_STRICT); + + // production/development mode detection + if (is_bool($mode)) { + self::$productionMode = $mode; + + } elseif ($mode !== self::DETECT || self::$productionMode === NULL) { // IP addresses or computer names whitelist detection + $list = is_string($mode) ? preg_split('#[,\s]+#', $mode) : (array) $mode; + if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $list[] = '127.0.0.1'; + $list[] = '::1'; + } + self::$productionMode = !in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE); + } + + // logging configuration + if (is_string($logDirectory)) { + self::$logDirectory = realpath($logDirectory); + if (self::$logDirectory === FALSE) { + die(__METHOD__ . "() error: Log directory is not found or is not directory."); + } + } elseif ($logDirectory === FALSE) { + self::$logDirectory = FALSE; + + } elseif (self::$logDirectory === NULL) { + self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log' : getcwd() . '/log'; + } + if (self::$logDirectory) { + ini_set('error_log', self::$logDirectory . '/php_error.log'); + } + + // php configuration + if (function_exists('ini_set')) { + ini_set('display_errors', !self::$productionMode); // or 'stderr' + ini_set('html_errors', FALSE); + ini_set('log_errors', FALSE); + + } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally == + die(__METHOD__ . "() error: Unable to set 'display_errors' because function ini_set() is disabled."); + } + + if ($email) { + if (!is_string($email)) { + die(__METHOD__ . '() error: Email address must be a string.'); + } + self::$email = $email; + } + + if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 8192); + } + + if (!defined('E_USER_DEPRECATED')) { + define('E_USER_DEPRECATED', 16384); + } + + if (!self::$enabled) { + register_shutdown_function(array(__CLASS__, '_shutdownHandler')); + set_exception_handler(array(__CLASS__, '_exceptionHandler')); + set_error_handler(array(__CLASS__, '_errorHandler')); + self::$enabled = TRUE; + } + } + + + + /** + * Is Debug enabled? + * @return bool + */ + public static function isEnabled() + { + return self::$enabled; + } + + + + /** + * Logs message or exception to file (if not disabled) and sends email notification (if enabled). + * @param string|Exception + * @param int one of constant Debugger::INFO, WARNING, ERROR (sends email), CRITICAL (sends email) + * @return string logged error filename + */ + public static function log($message, $priority = self::INFO) + { + if (self::$logDirectory === FALSE) { + return; + + } elseif (!self::$logDirectory) { + throw new Nette\InvalidStateException('Logging directory is not specified in Nette\Diagnostics\Debugger::$logDirectory.'); + } + + if ($message instanceof \Exception) { + $exception = $message; + $message = ($message instanceof Nette\FatalErrorException + ? 'Fatal error: ' . $exception->getMessage() + : get_class($exception) . ": " . $exception->getMessage()) + . " in " . $exception->getFile() . ":" . $exception->getLine(); + + $hash = md5($exception /*5.2*. (method_exists($exception, 'getPrevious') ? $exception->getPrevious() : (isset($exception->previous) ? $exception->previous : ''))*/); + $exceptionFilename = "exception-" . @date('Y-m-d-H-i-s') . "-$hash.html"; + foreach (new \DirectoryIterator(self::$logDirectory) as $entry) { + if (strpos($entry, $hash)) { + $exceptionFilename = $entry; + $saved = TRUE; + break; + } + } + } + + self::$logger->log(array( + @date('[Y-m-d H-i-s]'), + trim($message), + self::$source ? ' @ ' . self::$source : NULL, + !empty($exceptionFilename) ? ' @@ ' . $exceptionFilename : NULL + ), $priority); + + if (!empty($exceptionFilename)) { + $exceptionFilename = self::$logDirectory . '/' . $exceptionFilename; + if (empty($saved) && $logHandle = @fopen($exceptionFilename, 'w')) { + ob_start(); // double buffer prevents sending HTTP headers in some PHP + ob_start(function($buffer) use ($logHandle) { fwrite($logHandle, $buffer); }, 4096); + self::$blueScreen->render($exception); + ob_end_flush(); + ob_end_clean(); + fclose($logHandle); + } + return strtr($exceptionFilename, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR); + } + } + + + + /** + * Shutdown handler to catch fatal errors and execute of the planned activities. + * @return void + * @internal + */ + public static function _shutdownHandler() + { + if (!self::$enabled) { + return; + } + + // fatal error handler + static $types = array( + E_ERROR => 1, + E_CORE_ERROR => 1, + E_COMPILE_ERROR => 1, + E_PARSE => 1, + ); + $error = error_get_last(); + if (isset($types[$error['type']])) { + self::_exceptionHandler(new Nette\FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL)); + } + + // debug bar (require HTML & development mode) + if (self::$bar && !self::$productionMode && self::isHtmlMode()) { + self::$bar->render(); + } + } + + + + /** + * Handler to catch uncaught exception. + * @param \Exception + * @return void + * @internal + */ + public static function _exceptionHandler(\Exception $exception) + { + if (!headers_sent()) { // for PHP < 5.2.4 + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + header($protocol . ' 500', TRUE, 500); + } + + try { + if (self::$productionMode) { + try { + self::log($exception, self::ERROR); + } catch (\Exception $e) { + echo 'FATAL ERROR: unable to log error'; + } + + if (self::$consoleMode) { + echo "ERROR: the server encountered an internal error and was unable to complete your request.\n"; + + } elseif (self::isHtmlMode()) { + require __DIR__ . '/templates/error.phtml'; + } + + } else { + if (self::$consoleMode) { // dump to console + echo "$exception\n"; + if ($file = self::log($exception)) { + echo "(stored in $file)\n"; + if (self::$browser) { + exec(self::$browser . ' ' . escapeshellarg($file)); + } + } + + } elseif (self::isHtmlMode()) { // dump to browser + self::$blueScreen->render($exception); + if (self::$bar) { + self::$bar->render(); + } + + } elseif (!self::fireLog($exception, self::ERROR)) { // AJAX or non-HTML mode + $file = self::log($exception); + if (!headers_sent()) { + header("X-Nette-Error-Log: $file"); + } + } + } + + foreach (self::$onFatalError as $handler) { + call_user_func($handler, $exception); + } + + } catch (\Exception $e) { + if (self::$productionMode) { + echo self::isHtmlMode() ? 'FATAL ERROR' : 'FATAL ERROR'; + } else { + echo "FATAL ERROR: thrown ", get_class($e), ': ', $e->getMessage(), + "\nwhile processing ", get_class($exception), ': ', $exception->getMessage(), "\n"; + } + } + + self::$enabled = FALSE; // un-register shutdown function + exit(255); + } + + + + /** + * Handler to catch warnings and notices. + * @param int level of the error raised + * @param string error message + * @param string file that the error was raised in + * @param int line number the error was raised at + * @param array an array of variables that existed in the scope the error was triggered in + * @return bool FALSE to call normal error handler, NULL otherwise + * @throws Nette\FatalErrorException + * @internal + */ + public static function _errorHandler($severity, $message, $file, $line, $context) + { + if (self::$scream) { + error_reporting(E_ALL | E_STRICT); + } + + if (self::$lastError !== FALSE && ($severity & error_reporting()) === $severity) { // tryError mode + self::$lastError = new \ErrorException($message, 0, $severity, $file, $line); + return NULL; + } + + if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { + if (Helpers::findTrace(/*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE), '*::__toString')) { + $previous = isset($context['e']) && $context['e'] instanceof \Exception ? $context['e'] : NULL; + self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context, $previous)); + } + throw new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context); + + } elseif (($severity & error_reporting()) !== $severity) { + return FALSE; // calls normal error handler to fill-in error_get_last() + + } elseif (!self::$productionMode && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity))) { + self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context)); + } + + static $types = array( + E_WARNING => 'Warning', + E_COMPILE_WARNING => 'Warning', // currently unable to handle + E_USER_WARNING => 'Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'Notice', + E_STRICT => 'Strict standards', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'Deprecated', + ); + + $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message"; + $count = & self::$errorPanel->data["$message|$file|$line"]; + + if ($count++) { // repeated error + return NULL; + + } elseif (self::$productionMode) { + self::log("$message in $file:$line", self::ERROR); + return NULL; + + } else { + $ok = self::fireLog(new \ErrorException($message, 0, $severity, $file, $line), self::WARNING); + return !self::isHtmlMode() || (!self::$bar && !$ok) ? FALSE : NULL; + } + + return FALSE; // call normal error handler + } + + + + /** + * Handles exception thrown in __toString(). + * @param \Exception + * @return void + */ + public static function toStringException(\Exception $exception) + { + if (self::$enabled) { + self::_exceptionHandler($exception); + } else { + trigger_error($exception->getMessage(), E_USER_ERROR); + } + } + + + + /** + * Starts catching potential errors/warnings. + * @return void + */ + public static function tryError() + { + if (!self::$enabled && self::$lastError === FALSE) { + set_error_handler(array(__CLASS__, '_errorHandler')); + } + self::$lastError = NULL; + } + + + + /** + * Returns catched error/warning message. + * @param \ErrorException catched error + * @return bool + */ + public static function catchError(& $error) + { + if (!self::$enabled && self::$lastError !== FALSE) { + restore_error_handler(); + } + $error = self::$lastError; + self::$lastError = FALSE; + return (bool) $error; + } + + + + /********************* useful tools ****************d*g**/ + + + + /** + * Dumps information about a variable in readable format. + * @param mixed variable to dump + * @param bool return output instead of printing it? (bypasses $productionMode) + * @return mixed variable itself or dump + */ + public static function dump($var, $return = FALSE) + { + if (!$return && self::$productionMode) { + return $var; + } + + $output = "
    " . Helpers::htmlDump($var) . "
    \n"; + + if (!$return) { + $trace = /*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE); + $item = Helpers::findTrace($trace, 'dump') ?: Helpers::findTrace($trace, __CLASS__ . '::dump'); + if (isset($item['file'], $item['line']) && is_file($item['file'])) { + $lines = file($item['file']); + preg_match('#dump\((.*)\)#', $lines[$item['line'] - 1], $m); + $output = substr_replace( + $output, + ' title="' . htmlspecialchars((isset($m[0]) ? "$m[0] \n" : '') . "in file {$item['file']} on line {$item['line']}") . '"', + 4, 0); + + if (self::$showLocation) { + $output = substr_replace( + $output, + ' in ' . Helpers::editorLink($item['file'], $item['line']) . ":{$item['line']}", + -8, 0); + } + } + } + + if (self::$consoleMode) { + if (self::$consoleColors && substr(getenv('TERM'), 0, 5) === 'xterm') { + $output = preg_replace_callback('#|#', function($m) { + return "\033[" . (isset($m[1], Debugger::$consoleColors[$m[1]]) ? Debugger::$consoleColors[$m[1]] : '0') . "m"; + }, $output); + } + $output = htmlspecialchars_decode(strip_tags($output), ENT_QUOTES); + } + + if ($return) { + return $output; + + } else { + echo $output; + return $var; + } + } + + + + /** + * Starts/stops stopwatch. + * @param string name + * @return float elapsed seconds + */ + public static function timer($name = NULL) + { + static $time = array(); + $now = microtime(TRUE); + $delta = isset($time[$name]) ? $now - $time[$name] : 0; + $time[$name] = $now; + return $delta; + } + + + + /** + * Dumps information about a variable in Nette Debug Bar. + * @param mixed variable to dump + * @param string optional title + * @return mixed variable itself + */ + public static function barDump($var, $title = NULL) + { + if (!self::$productionMode) { + $dump = array(); + foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) { + $dump[$key] = Helpers::clickableDump($val); + } + self::$dumpPanel->data[] = array('title' => $title, 'dump' => $dump); + } + return $var; + } + + + + /** + * Sends message to FireLogger console. + * @param mixed message to log + * @return bool was successful? + */ + public static function fireLog($message) + { + if (!self::$productionMode) { + return self::$fireLogger->log($message); + } + } + + + + private static function isHtmlMode() + { + return !self::$ajaxDetected && !self::$consoleMode + && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())); + } + + + + /** @deprecated */ + public static function addPanel(IBarPanel $panel, $id = NULL) + { + return self::$bar->addPanel($panel, $id); + } + +} diff --git a/libs/Nette/Diagnostics/DefaultBarPanel.php b/libs/Nette/Diagnostics/DefaultBarPanel.php index 1779305..c969a79 100644 --- a/libs/Nette/Diagnostics/DefaultBarPanel.php +++ b/libs/Nette/Diagnostics/DefaultBarPanel.php @@ -1,76 +1,76 @@ -id = $id; - } - - - - /** - * Renders HTML code for custom tab. - * @return string - */ - public function getTab() - { - ob_start(); - $data = $this->data; - if ($this->id === 'time') { - require __DIR__ . '/templates/bar.time.tab.phtml'; - } elseif ($this->id === 'memory') { - require __DIR__ . '/templates/bar.memory.tab.phtml'; - } elseif ($this->id === 'dumps' && $this->data) { - require __DIR__ . '/templates/bar.dumps.tab.phtml'; - } elseif ($this->id === 'errors' && $this->data) { - require __DIR__ . '/templates/bar.errors.tab.phtml'; - } - return ob_get_clean(); - } - - - - /** - * Renders HTML code for custom panel. - * @return string - */ - public function getPanel() - { - ob_start(); - $data = $this->data; - if ($this->id === 'dumps') { - require __DIR__ . '/templates/bar.dumps.panel.phtml'; - } elseif ($this->id === 'errors') { - require __DIR__ . '/templates/bar.errors.panel.phtml'; - } - return ob_get_clean(); - } - -} +id = $id; + } + + + + /** + * Renders HTML code for custom tab. + * @return string + */ + public function getTab() + { + ob_start(); + $data = $this->data; + if ($this->id === 'time') { + require __DIR__ . '/templates/bar.time.tab.phtml'; + } elseif ($this->id === 'memory') { + require __DIR__ . '/templates/bar.memory.tab.phtml'; + } elseif ($this->id === 'dumps' && $this->data) { + require __DIR__ . '/templates/bar.dumps.tab.phtml'; + } elseif ($this->id === 'errors' && $this->data) { + require __DIR__ . '/templates/bar.errors.tab.phtml'; + } + return ob_get_clean(); + } + + + + /** + * Renders HTML code for custom panel. + * @return string + */ + public function getPanel() + { + ob_start(); + $data = $this->data; + if ($this->id === 'dumps') { + require __DIR__ . '/templates/bar.dumps.panel.phtml'; + } elseif ($this->id === 'errors') { + require __DIR__ . '/templates/bar.errors.panel.phtml'; + } + return ob_get_clean(); + } + +} diff --git a/libs/Nette/Diagnostics/FireLogger.php b/libs/Nette/Diagnostics/FireLogger.php index 2a23cd9..a8e9b3d 100644 --- a/libs/Nette/Diagnostics/FireLogger.php +++ b/libs/Nette/Diagnostics/FireLogger.php @@ -1,188 +1,188 @@ - array()); - - - - /** - * Sends message to FireLogger console. - * @param mixed - * @return bool was successful? - */ - public static function log($message, $priority = self::DEBUG) - { - if (!isset($_SERVER['HTTP_X_FIRELOGGER']) || headers_sent()) { - return FALSE; - } - - $item = array( - 'name' => 'PHP', - 'level' => $priority, - 'order' => count(self::$payload['logs']), - 'time' => str_pad(number_format((microtime(TRUE) - Debugger::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms', - 'template' => '', - 'message' => '', - 'style' => 'background:#767ab6', - ); - - $args = func_get_args(); - if (isset($args[0]) && is_string($args[0])) { - $item['template'] = array_shift($args); - } - - if (isset($args[0]) && $args[0] instanceof \Exception) { - $e = array_shift($args); - $trace = $e->getTrace(); - if (isset($trace[0]['class']) && $trace[0]['class'] === 'Nette\Diagnostics\Debugger' - && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler') - ) { - unset($trace[0]); - } - - $file = str_replace(dirname(dirname(dirname($e->getFile()))), "\xE2\x80\xA6", $e->getFile()); - $item['template'] = ($e instanceof \ErrorException ? '' : get_class($e) . ': ') - . $e->getMessage() . ($e->getCode() ? ' #' . $e->getCode() : '') . ' in ' . $file . ':' . $e->getLine(); - $item['pathname'] = $e->getFile(); - $item['lineno'] = $e->getLine(); - - } else { - $trace = debug_backtrace(); - if (isset($trace[1]['class']) && $trace[1]['class'] === 'Nette\Diagnostics\Debugger' - && ($trace[1]['function'] === 'fireLog') - ) { - unset($trace[0]); - } - - foreach ($trace as $frame) { - if (isset($frame['file']) && is_file($frame['file'])) { - $item['pathname'] = $frame['file']; - $item['lineno'] = $frame['line']; - break; - } - } - } - - $item['exc_info'] = array('', '', array()); - $item['exc_frames'] = array(); - - foreach ($trace as $frame) { - $frame += array('file' => NULL, 'line' => NULL, 'class' => NULL, 'type' => NULL, 'function' => NULL, 'object' => NULL, 'args' => NULL); - $item['exc_info'][2][] = array($frame['file'], $frame['line'], "$frame[class]$frame[type]$frame[function]", $frame['object']); - $item['exc_frames'][] = $frame['args']; - } - - if (isset($args[0]) && in_array($args[0], array(self::DEBUG, self::INFO, self::WARNING, self::ERROR, self::CRITICAL), TRUE)) { - $item['level'] = array_shift($args); - } - - $item['args'] = $args; - - self::$payload['logs'][] = self::jsonDump($item, -1); - foreach (str_split(base64_encode(@json_encode(self::$payload)), 4990) as $k => $v) { // intentionally @ - header("FireLogger-de11e-$k:$v"); - } - return TRUE; - } - - - - /** - * Dump implementation for JSON. - * @param mixed variable to dump - * @param int current recursion level - * @return string - */ - private static function jsonDump(&$var, $level = 0) - { - if (is_bool($var) || is_null($var) || is_int($var) || is_float($var)) { - return $var; - - } elseif (is_string($var)) { - if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { - $var = substr($var, 0, Debugger::$maxLen) . " \xE2\x80\xA6 "; - } - return Nette\Utils\Strings::fixEncoding($var); - - } elseif (is_array($var)) { - static $marker; - if ($marker === NULL) { - $marker = uniqid("\x00", TRUE); - } - if (isset($var[$marker])) { - return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; - - } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { - $var[$marker] = TRUE; - $res = array(); - foreach ($var as $k => &$v) { - if ($k !== $marker) { - $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); - } - } - unset($var[$marker]); - return $res; - - } else { - return " \xE2\x80\xA6 "; - } - - } elseif (is_object($var)) { - $arr = (array) $var; - static $list = array(); - if (in_array($var, $list, TRUE)) { - return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; - - } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { - $list[] = $var; - $res = array("\x00" => '(object) ' . get_class($var)); - foreach ($arr as $k => &$v) { - if ($k[0] === "\x00") { - $k = substr($k, strrpos($k, "\x00") + 1); - } - $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); - } - array_pop($list); - return $res; - - } else { - return " \xE2\x80\xA6 "; - } - - } elseif (is_resource($var)) { - return "resource " . get_resource_type($var); - - } else { - return "unknown type"; - } - } - -} + array()); + + + + /** + * Sends message to FireLogger console. + * @param mixed + * @return bool was successful? + */ + public static function log($message, $priority = self::DEBUG) + { + if (!isset($_SERVER['HTTP_X_FIRELOGGER']) || headers_sent()) { + return FALSE; + } + + $item = array( + 'name' => 'PHP', + 'level' => $priority, + 'order' => count(self::$payload['logs']), + 'time' => str_pad(number_format((microtime(TRUE) - Debugger::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms', + 'template' => '', + 'message' => '', + 'style' => 'background:#767ab6', + ); + + $args = func_get_args(); + if (isset($args[0]) && is_string($args[0])) { + $item['template'] = array_shift($args); + } + + if (isset($args[0]) && $args[0] instanceof \Exception) { + $e = array_shift($args); + $trace = $e->getTrace(); + if (isset($trace[0]['class']) && $trace[0]['class'] === 'Nette\Diagnostics\Debugger' + && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler') + ) { + unset($trace[0]); + } + + $file = str_replace(dirname(dirname(dirname($e->getFile()))), "\xE2\x80\xA6", $e->getFile()); + $item['template'] = ($e instanceof \ErrorException ? '' : get_class($e) . ': ') + . $e->getMessage() . ($e->getCode() ? ' #' . $e->getCode() : '') . ' in ' . $file . ':' . $e->getLine(); + $item['pathname'] = $e->getFile(); + $item['lineno'] = $e->getLine(); + + } else { + $trace = debug_backtrace(); + if (isset($trace[1]['class']) && $trace[1]['class'] === 'Nette\Diagnostics\Debugger' + && ($trace[1]['function'] === 'fireLog') + ) { + unset($trace[0]); + } + + foreach ($trace as $frame) { + if (isset($frame['file']) && is_file($frame['file'])) { + $item['pathname'] = $frame['file']; + $item['lineno'] = $frame['line']; + break; + } + } + } + + $item['exc_info'] = array('', '', array()); + $item['exc_frames'] = array(); + + foreach ($trace as $frame) { + $frame += array('file' => NULL, 'line' => NULL, 'class' => NULL, 'type' => NULL, 'function' => NULL, 'object' => NULL, 'args' => NULL); + $item['exc_info'][2][] = array($frame['file'], $frame['line'], "$frame[class]$frame[type]$frame[function]", $frame['object']); + $item['exc_frames'][] = $frame['args']; + } + + if (isset($args[0]) && in_array($args[0], array(self::DEBUG, self::INFO, self::WARNING, self::ERROR, self::CRITICAL), TRUE)) { + $item['level'] = array_shift($args); + } + + $item['args'] = $args; + + self::$payload['logs'][] = self::jsonDump($item, -1); + foreach (str_split(base64_encode(@json_encode(self::$payload)), 4990) as $k => $v) { // intentionally @ + header("FireLogger-de11e-$k:$v"); + } + return TRUE; + } + + + + /** + * Dump implementation for JSON. + * @param mixed variable to dump + * @param int current recursion level + * @return string + */ + private static function jsonDump(&$var, $level = 0) + { + if (is_bool($var) || is_null($var) || is_int($var) || is_float($var)) { + return $var; + + } elseif (is_string($var)) { + if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { + $var = substr($var, 0, Debugger::$maxLen) . " \xE2\x80\xA6 "; + } + return Nette\Utils\Strings::fixEncoding($var); + + } elseif (is_array($var)) { + static $marker; + if ($marker === NULL) { + $marker = uniqid("\x00", TRUE); + } + if (isset($var[$marker])) { + return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { + $var[$marker] = TRUE; + $res = array(); + foreach ($var as $k => &$v) { + if ($k !== $marker) { + $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); + } + } + unset($var[$marker]); + return $res; + + } else { + return " \xE2\x80\xA6 "; + } + + } elseif (is_object($var)) { + $arr = (array) $var; + static $list = array(); + if (in_array($var, $list, TRUE)) { + return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { + $list[] = $var; + $res = array("\x00" => '(object) ' . get_class($var)); + foreach ($arr as $k => &$v) { + if ($k[0] === "\x00") { + $k = substr($k, strrpos($k, "\x00") + 1); + } + $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); + } + array_pop($list); + return $res; + + } else { + return " \xE2\x80\xA6 "; + } + + } elseif (is_resource($var)) { + return "resource " . get_resource_type($var); + + } else { + return "unknown type"; + } + } + +} diff --git a/libs/Nette/Diagnostics/Helpers.php b/libs/Nette/Diagnostics/Helpers.php index ed1520f..0315555 100644 --- a/libs/Nette/Diagnostics/Helpers.php +++ b/libs/Nette/Diagnostics/Helpers.php @@ -1,196 +1,233 @@ -href(strtr(Debugger::$editor, array('%file' => rawurlencode($file), '%line' => $line))); - } else { - $el = Nette\Utils\Html::el('span'); - } - return $el->title("$file:$line") - ->setHtml(htmlSpecialChars(rtrim($dir, DIRECTORY_SEPARATOR)) . DIRECTORY_SEPARATOR . '' . htmlSpecialChars(basename($file)) . ''); - } - - - - /** - * Internal dump() implementation. - * @param mixed variable to dump - * @param int current recursion level - * @return string - */ - public static function htmlDump(&$var, $level = 0) - { - static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u'; - if ($tableUtf === NULL) { - foreach (range("\x00", "\xFF") as $ch) { - if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) { - $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT); - } elseif (ord($ch) < 127) { - $tableUtf[$ch] = $tableBin[$ch] = $ch; - } else { - $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); - } - } - $tableBin["\\"] = '\\\\'; - $tableBin["\r"] = '\\r'; - $tableBin["\n"] = '\\n'; - $tableBin["\t"] = '\\t'; - $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x'; - } - - if (is_bool($var)) { - return ($var ? 'TRUE' : 'FALSE') . "\n"; - - } elseif ($var === NULL) { - return "NULL\n"; - - } elseif (is_int($var)) { - return "$var\n"; - - } elseif (is_float($var)) { - $var = var_export($var, TRUE); - if (strpos($var, '.') === FALSE) { - $var .= '.0'; - } - return "$var\n"; - - } elseif (is_string($var)) { - if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { - $s = htmlSpecialChars(substr($var, 0, Debugger::$maxLen), ENT_NOQUOTES) . ' ... '; - } else { - $s = htmlSpecialChars($var, ENT_NOQUOTES); - } - $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf); - $len = strlen($var); - return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n"; - - } elseif (is_array($var)) { - $s = "array(" . count($var) . ") "; - $space = str_repeat($space1 = ' ', $level); - $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}"; - - static $marker; - if ($marker === NULL) { - $marker = uniqid("\x00", TRUE); - } - if (empty($var)) { - - } elseif (isset($var[$marker])) { - $brackets = $var[$marker]; - $s .= "$brackets[0] *RECURSION* $brackets[1]"; - - } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { - $s .= "$brackets[0]\n"; - $var[$marker] = $brackets; - foreach ($var as $k => &$v) { - if ($k === $marker) { - continue; - } - $k = is_int($k) ? $k : '"' . htmlSpecialChars(strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf)) . '"'; - $s .= "$space$space1$k => " . self::htmlDump($v, $level + 1); - } - unset($var[$marker]); - $s .= "$space$brackets[1]"; - - } else { - $s .= "$brackets[0] ... $brackets[1]"; - } - return $s . "\n"; - - } elseif (is_object($var)) { - $arr = (array) $var; - $s = "" . get_class($var) . "(" . count($arr) . ") "; - $space = str_repeat($space1 = ' ', $level); - - static $list = array(); - if (empty($arr)) { - - } elseif (in_array($var, $list, TRUE)) { - $s .= "{ *RECURSION* }"; - - } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { - $s .= "{\n"; - $list[] = $var; - foreach ($arr as $k => &$v) { - $m = ''; - if ($k[0] === "\x00") { - $m = $k[1] === '*' ? ' protected' : ' private'; - $k = substr($k, strrpos($k, "\x00") + 1); - } - $k = htmlSpecialChars(strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf)); - $s .= "$space$space1\"$k\"$m => " . self::htmlDump($v, $level + 1); - } - array_pop($list); - $s .= "$space}"; - - } else { - $s .= "{ ... }"; - } - return $s . "\n"; - - } elseif (is_resource($var)) { - return "" . htmlSpecialChars(get_resource_type($var)) . " resource\n"; - - } else { - return "unknown type\n"; - } - } - - - - /** - * Dumps variable. - * @param string - * @return string - */ - public static function clickableDump($dump) - { - return '
    ' . preg_replace_callback(
    -			'#^( *)((?>[^(]{1,200}))\((\d+)\) #m',
    -			function ($m) {
    -				return "$m[1]'
    -					: ' ');
    -			},
    -			self::htmlDump($dump)
    -		) . '
    '; - } - -} +href(strtr(Debugger::$editor, array('%file' => rawurlencode($file), '%line' => $line))) + ->title("$file:$line") + ->setHtml(htmlSpecialChars(rtrim($dir, DIRECTORY_SEPARATOR)) . DIRECTORY_SEPARATOR . '' . htmlSpecialChars(basename($file)) . ''); + } else { + return Nette\Utils\Html::el('span')->setText($file); + } + } + + + + /** + * Internal dump() implementation. + * @param mixed variable to dump + * @param int current recursion level + * @return string + */ + public static function htmlDump(&$var, $level = 0) + { + static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u'; + if ($tableUtf === NULL) { + foreach (range("\x00", "\xFF") as $ch) { + if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) { + $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT); + } elseif (ord($ch) < 127) { + $tableUtf[$ch] = $tableBin[$ch] = $ch; + } else { + $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); + } + } + $tableBin["\\"] = '\\\\'; + $tableBin["\r"] = '\\r'; + $tableBin["\n"] = '\\n'; + $tableBin["\t"] = '\\t'; + $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x'; + } + + if (is_bool($var)) { + return '' . ($var ? 'TRUE' : 'FALSE') . "\n"; + + } elseif ($var === NULL) { + return "NULL\n"; + + } elseif (is_int($var)) { + return "$var\n"; + + } elseif (is_float($var)) { + $var = var_export($var, TRUE); + if (strpos($var, '.') === FALSE) { + $var .= '.0'; + } + return "$var\n"; + + } elseif (is_string($var)) { + if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { + $s = htmlSpecialChars(substr($var, 0, Debugger::$maxLen), ENT_NOQUOTES, 'ISO-8859-1') . ' ... '; + } else { + $s = htmlSpecialChars($var, ENT_NOQUOTES, 'ISO-8859-1'); + } + $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf); + $len = strlen($var); + return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n"; + + } elseif (is_array($var)) { + $s = 'array(' . count($var) . ") "; + $space = str_repeat($space1 = ' ', $level); + $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}"; + + static $marker; + if ($marker === NULL) { + $marker = uniqid("\x00", TRUE); + } + if (empty($var)) { + + } elseif (isset($var[$marker])) { + $brackets = $var[$marker]; + $s .= "$brackets[0] *RECURSION* $brackets[1]"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { + $s .= "$brackets[0]\n"; + $var[$marker] = $brackets; + foreach ($var as $k => &$v) { + if ($k === $marker) { + continue; + } + $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf); + $k = htmlSpecialChars(preg_match('#^\w+$#', $k) ? $k : "\"$k\""); + $s .= "$space$space1$k => " . self::htmlDump($v, $level + 1); + } + unset($var[$marker]); + $s .= "$space$brackets[1]"; + + } else { + $s .= "$brackets[0] ... $brackets[1]"; + } + return $s . "\n"; + + } elseif (is_object($var)) { + if ($var instanceof \Closure) { + $rc = new \ReflectionFunction($var); + $arr = array(); + foreach ($rc->getParameters() as $param) { + $arr[] = '$' . $param->getName(); + } + $arr = array('file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'parameters' => implode(', ', $arr)); + } else { + $arr = (array) $var; + } + $s = '' . get_class($var) . "(" . count($arr) . ") "; + $space = str_repeat($space1 = ' ', $level); + + static $list = array(); + if (empty($arr)) { + + } elseif (in_array($var, $list, TRUE)) { + $s .= "{ *RECURSION* }"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth || $var instanceof \Closure) { + $s .= "{\n"; + $list[] = $var; + foreach ($arr as $k => &$v) { + $m = ''; + if ($k[0] === "\x00") { + $m = ' ' . ($k[1] === '*' ? 'protected' : 'private') . ''; + $k = substr($k, strrpos($k, "\x00") + 1); + } + $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf); + $k = htmlSpecialChars(preg_match('#^\w+$#', $k) ? $k : "\"$k\""); + $s .= "$space$space1$k$m => " . self::htmlDump($v, $level + 1); + } + array_pop($list); + $s .= "$space}"; + + } else { + $s .= "{ ... }"; + } + return $s . "\n"; + + } elseif (is_resource($var)) { + $type = get_resource_type($var); + $s = '' . htmlSpecialChars($type) . " resource "; + + static $info = array('stream' => 'stream_get_meta_data', 'curl' => 'curl_getinfo'); + if (isset($info[$type])) { + $space = str_repeat($space1 = ' ', $level); + $s .= "{\n"; + foreach (call_user_func($info[$type], $var) as $k => $v) { + $s .= $space . $space1 . '' . htmlSpecialChars($k) . " => " . self::htmlDump($v, $level + 1); + } + $s .= "$space}"; + } + return $s . "\n"; + + } else { + return "unknown type\n"; + } + } + + + + /** + * Dumps variable. + * @param string + * @return string + */ + public static function clickableDump($dump, $collapsed = FALSE) + { + return '
    ' . preg_replace_callback(
    +			'#^( *)((?>[^(\r\n]{1,200}))\((\d+)\) #m',
    +			function($m) use ($collapsed) {
    +				return "$m[1]'
    +					: ' ');
    +			},
    +			self::htmlDump($dump)
    +		) . '
    '; + } + + + + public static function findTrace(array $trace, $method, & $index = NULL) + { + $m = explode('::', $method); + foreach ($trace as $i => $item) { + if (isset($item['function']) && $item['function'] === end($m) + && isset($item['class']) === isset($m[1]) + && (!isset($item['class']) || $item['class'] === $m[0] || $m[0] === '*' || is_subclass_of($item['class'], $m[0])) + ) { + $index = $i; + return $item; + } + } + } + +} diff --git a/libs/Nette/Diagnostics/IBarPanel.php b/libs/Nette/Diagnostics/IBarPanel.php index f959503..97262b5 100644 --- a/libs/Nette/Diagnostics/IBarPanel.php +++ b/libs/Nette/Diagnostics/IBarPanel.php @@ -1,38 +1,38 @@ -directory)) { - throw new Nette\DirectoryNotFoundException("Directory '$this->directory' is not found or is not directory."); - } - - if (is_array($message)) { - $message = implode(' ', $message); - } - $res = error_log(trim($message) . PHP_EOL, 3, $this->directory . '/' . strtolower($priority) . '.log'); - - if (($priority === self::ERROR || $priority === self::CRITICAL) && $this->email && $this->mailer - && @filemtime($this->directory . '/email-sent') + self::$emailSnooze < time() // @ - file may not exist - && @file_put_contents($this->directory . '/email-sent', 'sent') // @ - file may not be writable - ) { - call_user_func($this->mailer, $message, $this->email); - } - return $res; - } - - - - /** - * Default mailer. - * @param string - * @param string - * @return void - */ - private static function defaultMailer($message, $email) - { - $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : - (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''); - - $parts = str_replace( - array("\r\n", "\n"), - array("\n", PHP_EOL), - array( - 'headers' => "From: noreply@$host\nX-Mailer: Nette Framework\n", - 'subject' => "PHP: An error occurred on the server $host", - 'body' => "[" . @date('Y-m-d H:i:s') . "] $message", // @ - timezone may not be set - ) - ); - - mail($email, $parts['subject'], $parts['body'], $parts['headers']); - } - -} +directory)) { + throw new Nette\DirectoryNotFoundException("Directory '$this->directory' is not found or is not directory."); + } + + if (is_array($message)) { + $message = implode(' ', $message); + } + $res = error_log(trim($message) . PHP_EOL, 3, $this->directory . '/' . strtolower($priority) . '.log'); + + if (($priority === self::ERROR || $priority === self::CRITICAL) && $this->email && $this->mailer + && @filemtime($this->directory . '/email-sent') + self::$emailSnooze < time() // @ - file may not exist + && @file_put_contents($this->directory . '/email-sent', 'sent') // @ - file may not be writable + ) { + Nette\Callback::create($this->mailer)->invoke($message, $this->email); + } + return $res; + } + + + + /** + * Default mailer. + * @param string + * @param string + * @return void + */ + public static function defaultMailer($message, $email) + { + $host = php_uname('n'); + foreach (array('HTTP_HOST','SERVER_NAME', 'HOSTNAME') as $item) { + if (isset($_SERVER[$item])) { + $host = $_SERVER[$item]; break; + } + } + + $parts = str_replace( + array("\r\n", "\n"), + array("\n", PHP_EOL), + array( + 'headers' => implode("\n", array( + "From: noreply@$host", + 'X-Mailer: Nette Framework', + 'Content-Type: text/plain; charset=UTF-8', + 'Content-Transfer-Encoding: 8bit', + )) . "\n", + 'subject' => "PHP: An error occurred on the server $host", + 'body' => "[" . @date('Y-m-d H:i:s') . "] $message", // @ - timezone may not be set + ) + ); + + mail($email, $parts['subject'], $parts['body'], $parts['headers']); + } + +} diff --git a/libs/Nette/Diagnostics/shortcuts.php b/libs/Nette/Diagnostics/shortcuts.php new file mode 100644 index 0000000..b18d20e --- /dev/null +++ b/libs/Nette/Diagnostics/shortcuts.php @@ -0,0 +1,25 @@ + - - - -

    Dumped variables

    - -
    - - -

    - - - - - $dump): ?> - - - - - -
    - -
    + + + + +

    Dumped variables

    + +
    + + +

    + + + + + $dump): ?> + + + + + +
    + +
    diff --git a/libs/Nette/Diagnostics/templates/bar.dumps.tab.phtml b/libs/Nette/Diagnostics/templates/bar.dumps.tab.phtml index fd6a0c7..72c5bcb 100644 --- a/libs/Nette/Diagnostics/templates/bar.dumps.tab.phtml +++ b/libs/Nette/Diagnostics/templates/bar.dumps.tab.phtml @@ -1,14 +1,14 @@ - -variables + +variables diff --git a/libs/Nette/Diagnostics/templates/bar.errors.panel.phtml b/libs/Nette/Diagnostics/templates/bar.errors.panel.phtml index c0b7388..9dbc604 100644 --- a/libs/Nette/Diagnostics/templates/bar.errors.panel.phtml +++ b/libs/Nette/Diagnostics/templates/bar.errors.panel.phtml @@ -1,26 +1,26 @@ - -

    Errors

    - -
    - - - $count): list($message, $file, $line) = explode('|', $item) ?> - - - - - -
    -
    + +

    Errors

    + +
    + + + $count): list($message, $file, $line) = explode('|', $item) ?> + + + + + +
    +
    diff --git a/libs/Nette/Diagnostics/templates/bar.errors.tab.phtml b/libs/Nette/Diagnostics/templates/bar.errors.tab.phtml index 35fb7bc..4eb5b15 100644 --- a/libs/Nette/Diagnostics/templates/bar.errors.tab.phtml +++ b/libs/Nette/Diagnostics/templates/bar.errors.tab.phtml @@ -1,15 +1,15 @@ - - errors + + errors diff --git a/libs/Nette/Diagnostics/templates/bar.memory.tab.phtml b/libs/Nette/Diagnostics/templates/bar.memory.tab.phtml index 6998880..b434cc4 100644 --- a/libs/Nette/Diagnostics/templates/bar.memory.tab.phtml +++ b/libs/Nette/Diagnostics/templates/bar.memory.tab.phtml @@ -1,15 +1,15 @@ - - MB + + MB diff --git a/libs/Nette/Diagnostics/templates/bar.phtml b/libs/Nette/Diagnostics/templates/bar.phtml index f089a4c..6735180 100644 --- a/libs/Nette/Diagnostics/templates/bar.phtml +++ b/libs/Nette/Diagnostics/templates/bar.phtml @@ -1,102 +1,621 @@ - - - - - - - - -  - - - - - - - - - - $panel): if (!$panel['panel']) continue; ?> -
    -
    - -
    - ¤ - × -
    -
    -
    - - -
    -
    -
      - - -
    • ', trim($panel['tab']), ''; endif ?>
    • - -
    • ×
    • -
    -
    -
    - - - -
    - - - - + + + + + + + + +  + + + + + + + + + + $panel): if (!$panel['panel']) continue; ?> +
    +
    + +
    + ¤ + × +
    +
    +
    + + +
    +
    +
      + + +
    • ', trim($panel['tab']), ''; endif ?>
    • + +
    • ×
    • +
    +
    +
    + + + +
    + + + + diff --git a/libs/Nette/Diagnostics/templates/bar.time.tab.phtml b/libs/Nette/Diagnostics/templates/bar.time.tab.phtml index b7a3d22..4bcd33b 100644 --- a/libs/Nette/Diagnostics/templates/bar.time.tab.phtml +++ b/libs/Nette/Diagnostics/templates/bar.time.tab.phtml @@ -1,15 +1,15 @@ - - ms + + ms diff --git a/libs/Nette/Diagnostics/templates/bluescreen.phtml b/libs/Nette/Diagnostics/templates/bluescreen.phtml index 5ba38a3..2e9cc9e 100644 --- a/libs/Nette/Diagnostics/templates/bluescreen.phtml +++ b/libs/Nette/Diagnostics/templates/bluescreen.phtml @@ -1,332 +1,644 @@ - 'Fatal Error', - E_USER_ERROR => 'User Error', - E_RECOVERABLE_ERROR => 'Recoverable Error', - E_CORE_ERROR => 'Core Error', - E_COMPILE_ERROR => 'Compile Error', - E_PARSE => 'Parse Error', - E_WARNING => 'Warning', - E_CORE_WARNING => 'Core Warning', - E_COMPILE_WARNING => 'Compile Warning', - E_USER_WARNING => 'User Warning', - E_NOTICE => 'Notice', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Strict', - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User Deprecated', -); - -$title = ($exception instanceof Nette\FatalErrorException && isset($errorTypes[$exception->getSeverity()])) ? $errorTypes[$exception->getSeverity()] : get_class($exception); - -$expandPath = NETTE_DIR . DIRECTORY_SEPARATOR; // . 'Utils' . DIRECTORY_SEPARATOR . 'Object'; -$counter = 0; - -?> - - - - - - - - <?php echo htmlspecialchars($title) ?> - - - - - - - -
    -
    -
    -

    getCode() ? ' #' . $exception->getCode() : '') ?>

    - -

    getMessage()) ?> getMessage())) ?>" id="netteBsSearch">search►

    -
    - - - - - - - -
    -

    Caused by 2) ? '►' : '▼' ?>

    - -
    -
    -

    getCode() ? ' #' . $ex->getCode() : '') ?>

    - -

    getMessage()) ?>

    -
    - - - - - - -
    -

    - -
    - -
    - - - - - getTrace(); $expanded = NULL ?> - getFile(), $expandPath) === 0) { - foreach ($stack as $key => $row) { - if (isset($row['file']) && strpos($row['file'], $expandPath) !== 0) { $expanded = $key; break; } - } - } ?> - getFile())): ?> -
    -

    Source file

    - -
    -

    File: getFile(), $ex->getLine()) ?>   Line: getLine() ?>

    -
    getFile(), $ex->getLine()) ?>
    -
    - - - - - - -
    -

    Call stack

    - -
    -
      - $row): ?> -
    1. - - - - - inner-code - - - ">source   - - - - - (">arguments ) -

      - - -
      "> - - getParameters(); - } catch (\Exception $e) { - $params = array(); - } - foreach ($row['args'] as $k => $v) { - echo '\n"; - } - ?> -
      ', (isset($params[$k]) ? '$' . $params[$k]->name : "#$k"), ''; - echo Helpers::clickableDump($v); - echo "
      -
      - - - - -
       id="netteBsSrc">
      - - -
    2. - -
    -
    - - - - - context) && is_array($ex->context)):?> -
    -

    Variables

    - -
    -
    - - context as $k => $v) { - echo '\n"; - } - ?> -
    $', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    -
    -
    - - - getPrevious()) || (isset($ex->previous) && $ex = $ex->previous)); ?> -
    ' ?> - - - - - -
    -

    - -
    - -
    - - - - -
    -

    Environment

    - -
    - -

    Constants

    -
    - - $v) { - echo ''; - echo '\n"; - } - ?> -
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    -
    - - - -

    Included files ()

    -
    - - \n"; - } - ?> -
    ', htmlspecialchars($v), "
    -
    - - -

    $_SERVER

    - -

    empty

    - -
    - - $v) echo '\n"; - ?> -
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    -
    - -
    - - - -
    -

    HTTP request

    - -
    - -

    Headers

    -
    - - $v) echo '\n"; - ?> -
    ', htmlspecialchars($k), '', htmlspecialchars($v), "
    -
    - - - - -

    $

    - -

    empty

    - -
    - - $v) echo '\n"; - ?> -
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    -
    - - -
    - - - -
    -

    HTTP response

    - -
    -

    Headers

    - -
    ';
    -			?>
    - -

    no headers

    - -
    - - -
      -
    • Report generated at
    • - -
    • - -
    • - -
    • PHP
    • -
    • -
    • (revision )
    • -
    -
    -
    - - - - + 'Fatal Error', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Recoverable Error', + E_CORE_ERROR => 'Core Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse Error', + E_WARNING => 'Warning', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_WARNING => 'User Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Strict', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', +); + +$title = ($exception instanceof Nette\FatalErrorException && isset($errorTypes[$exception->getSeverity()])) ? $errorTypes[$exception->getSeverity()] : get_class($exception); + +$expandPath = NETTE_DIR . DIRECTORY_SEPARATOR; // . 'Utils' . DIRECTORY_SEPARATOR . 'Object'; +$counter = 0; + +?> + + + + + + + <?php echo htmlspecialchars($title) ?> + + + + + + + +
    +
    +
    +

    getCode() ? ' #' . $exception->getCode() : '') ?>

    + +

    getMessage()) ?> getMessage())) ?>" id="netteBsSearch">search►

    +
    + + + + + + + +
    +

    Caused by 2) ? '►' : '▼' ?>

    + +
    +
    +

    getCode() ? ' #' . $ex->getCode() : '')) ?>

    + +

    getMessage()) ?>

    +
    + + + + + + + +
    +

    + +
    + +
    + + + + + getTrace(); $expanded = NULL ?> + getFile(), $expandPath) === 0) { + foreach ($stack as $key => $row) { + if (isset($row['file']) && strpos($row['file'], $expandPath) !== 0) { $expanded = $key; break; } + } + } ?> + +
    +

    Source file

    + +
    +

    File: getFile(), $ex->getLine()) ?>   Line: getLine() ?>

    + getFile())): ?>getFile(), $ex->getLine(), 15, isset($ex->context) ? $ex->context : NULL) ?> +
    + + + + + +
    +

    Call stack

    + +
    +
      + $row): ?> +
    1. + + + + + inner-code + + + ">source   + + + + + (">arguments ) +

      + + +
      "> + + getParameters(); + } catch (\Exception $e) { + $params = array(); + } + foreach ($row['args'] as $k => $v) { + echo '\n"; + } + ?> +
      ', htmlspecialchars(isset($params[$k]) ? '$' . $params[$k]->name : "#$k"), ''; + echo Helpers::clickableDump($v); + echo "
      +
      + + + + +
      id="netteBsSrc">
      + + +
    2. + +
    +
    + + + + + context) && is_array($ex->context)):?> +
    +

    Variables

    + +
    +
    + + context as $k => $v) { + echo '\n"; + } + ?> +
    $', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    +
    +
    + + + getPrevious()) || (isset($ex->previous) && $ex = $ex->previous)); ?> +
    ' ?> + + + + + + + +
    +

    + +
    + +
    + + + + +
    +

    Environment

    + +
    +

    $_SERVER

    +
    + + $v) echo '\n"; + ?> +
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    +
    + + +

    $_SESSION

    +
    + +

    empty

    + + + $v) echo '\n"; + ?> +
    ', htmlspecialchars($k), '', $k === '__NF' ? 'Nette Session' : Helpers::clickableDump($v), "
    + +
    + + + +

    Nette Session

    +
    + + $v) echo '\n"; + ?> +
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    +
    + + + + +

    Constants

    +
    + + $v) { + echo ''; + echo '\n"; + } + ?> +
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    +
    + + + +

    Included files ()

    +
    + + \n"; + } + ?> +
    ', htmlspecialchars($v), "
    +
    + + +

    Configuration options

    +
    + |.+$#s', '', ob_get_clean()) ?> +
    +
    + + + +
    +

    HTTP request

    + +
    + +

    Headers

    +
    + + $v) echo '\n"; + ?> +
    ', htmlspecialchars($k), '', htmlspecialchars($v), "
    +
    + + + + +

    $

    + +

    empty

    + +
    + + $v) echo '\n"; + ?> +
    ', htmlspecialchars($k), '', Helpers::clickableDump($v), "
    +
    + + +
    + + + +
    +

    HTTP response

    + +
    +

    Headers

    + +
    ';
    +			?>
    + +

    no headers

    + +
    + + + + +
    +

    + +
    + +
    + + + + +
      +
    • Report generated at
    • + +
    • + +
    • + +
    • PHP
    • +
    • +
    • (revision )
    • +
    +
    +
    + + + + diff --git a/libs/Nette/Diagnostics/templates/error.phtml b/libs/Nette/Diagnostics/templates/error.phtml index 612b088..9b0f1d9 100644 --- a/libs/Nette/Diagnostics/templates/error.phtml +++ b/libs/Nette/Diagnostics/templates/error.phtml @@ -1,21 +1,26 @@ - - - - - - -Server Error - -

    Server Error

    - -

    We're sorry! The server encountered an internal error and was unable to complete your request. Please try again later.

    - -

    error 500

    + + + + + + +Server Error + +

    Server Error

    + +

    We're sorry! The server encountered an internal error and was unable to complete your request. Please try again later.

    + +

    error 500

    diff --git a/libs/Nette/Diagnostics/templates/netteQ.js b/libs/Nette/Diagnostics/templates/netteQ.js index 802272f..68cf3a4 100644 --- a/libs/Nette/Diagnostics/templates/netteQ.js +++ b/libs/Nette/Diagnostics/templates/netteQ.js @@ -1,332 +1,332 @@ -/** - * NetteQ - * - * This file is part of the Nette Framework. - * Copyright (c) 2004, 2011 David Grudl (http://davidgrudl.com) - */ - -var Nette = Nette || {}; - -(function(){ - -// simple class builder -Nette.Class = function(def) { - var cl = def.constructor || function(){}, nm, __hasProp = Object.prototype.hasOwnProperty; - delete def.constructor; - - if (def.Extends) { - var foo = function() { this.constructor = cl; }; - foo.prototype = def.Extends.prototype; - cl.prototype = new foo(); - delete def.Extends; - } - - if (def.Static) { - for (nm in def.Static) { if (__hasProp.call(def.Static, nm)) cl[nm] = def.Static[nm]; } - delete def.Static; - } - - for (nm in def) { if (__hasProp.call(def, nm)) cl.prototype[nm] = def[nm]; } - return cl; -}; - - -// supported cross-browser selectors: #id | div | div.class | .class -Nette.Q = Nette.Class({ - - Static: { - factory: function(selector) { - return new Nette.Q(selector); - }, - - implement: function(methods) { - var nm, fn = Nette.Q.implement, prot = Nette.Q.prototype, __hasProp = Object.prototype.hasOwnProperty; - for (nm in methods) { - if (!__hasProp.call(methods, nm)) { - continue; - } - fn[nm] = methods[nm]; - prot[nm] = (function(nm){ - return function() { return this.each(fn[nm], arguments); }; - }(nm)); - } - } - }, - - constructor: function(selector) { - if (typeof selector === "string") { - selector = this._find(document, selector); - - } else if (!selector || selector.nodeType || selector.length === undefined || selector === window) { - selector = [selector]; - } - - for (var i = 0, len = selector.length; i < len; i++) { - if (selector[i]) { this[this.length++] = selector[i]; } - } - }, - - length: 0, - - find: function(selector) { - return new Nette.Q(this._find(this[0], selector)); - }, - - _find: function(context, selector) { - if (!context || !selector) { - return []; - - } else if (document.querySelectorAll) { - return context.querySelectorAll(selector); - - } else if (selector.charAt(0) === '#') { // #id - return [document.getElementById(selector.substring(1))]; - - } else { // div | div.class | .class - selector = selector.split('.'); - var elms = context.getElementsByTagName(selector[0] || '*'); - - if (selector[1]) { - var list = [], pattern = new RegExp('(^|\\s)' + selector[1] + '(\\s|$)'); - for (var i = 0, len = elms.length; i < len; i++) { - if (pattern.test(elms[i].className)) { list.push(elms[i]); } - } - return list; - } else { - return elms; - } - } - }, - - dom: function() { - return this[0]; - }, - - each: function(callback, args) { - for (var i = 0, res; i < this.length; i++) { - if ((res = callback.apply(this[i], args || [])) !== undefined) { return res; } - } - return this; - } -}); - - -var $ = Nette.Q.factory, fn = Nette.Q.implement; - -fn({ - // cross-browser event attach - bind: function(event, handler) { - if (document.addEventListener && (event === 'mouseenter' || event === 'mouseleave')) { // simulate mouseenter & mouseleave using mouseover & mouseout - var old = handler; - event = event === 'mouseenter' ? 'mouseover' : 'mouseout'; - handler = function(e) { - for (var target = e.relatedTarget; target; target = target.parentNode) { - if (target === this) { return; } // target must not be inside this - } - old.call(this, e); - }; - } - - var data = fn.data.call(this), - events = data.events = data.events || {}; // use own handler queue - - if (!events[event]) { - var el = this, // fixes 'this' in iE - handlers = events[event] = [], - generic = fn.bind.genericHandler = function(e) { // dont worry, 'e' is passed in IE - if (!e.target) { - e.target = e.srcElement; - } - if (!e.preventDefault) { - e.preventDefault = function() { e.returnValue = false; }; - } - if (!e.stopPropagation) { - e.stopPropagation = function() { e.cancelBubble = true; }; - } - e.stopImmediatePropagation = function() { this.stopPropagation(); i = handlers.length; }; - for (var i = 0; i < handlers.length; i++) { - handlers[i].call(el, e); - } - }; - - if (document.addEventListener) { // non-IE - this.addEventListener(event, generic, false); - } else if (document.attachEvent) { // IE < 9 - this.attachEvent('on' + event, generic); - } - } - - events[event].push(handler); - }, - - // adds class to element - addClass: function(className) { - this.className = this.className.replace(/^|\s+|$/g, ' ').replace(' '+className+' ', ' ') + ' ' + className; - }, - - // removes class from element - removeClass: function(className) { - this.className = this.className.replace(/^|\s+|$/g, ' ').replace(' '+className+' ', ' '); - }, - - // tests whether element has given class - hasClass: function(className) { - return this.className.replace(/^|\s+|$/g, ' ').indexOf(' '+className+' ') > -1; - }, - - show: function() { - var dsp = fn.show.display = fn.show.display || {}, tag = this.tagName; - if (!dsp[tag]) { - var el = document.body.appendChild(document.createElement(tag)); - dsp[tag] = fn.css.call(el, 'display'); - } - this.style.display = dsp[tag]; - }, - - hide: function() { - this.style.display = 'none'; - }, - - css: function(property) { - return this.currentStyle ? this.currentStyle[property] - : (window.getComputedStyle ? document.defaultView.getComputedStyle(this, null).getPropertyValue(property) : undefined); - }, - - data: function() { - return this.nette ? this.nette : this.nette = {}; - }, - - val: function() { - var i; - if (!this.nodeName) { // radio - for (i = 0, len = this.length; i < len; i++) { - if (this[i].checked) { return this[i].value; } - } - return null; - } - - if (this.nodeName.toLowerCase() === 'select') { - var index = this.selectedIndex, options = this.options; - - if (index < 0) { - return null; - - } else if (this.type === 'select-one') { - return options[index].value; - } - - for (i = 0, values = [], len = options.length; i < len; i++) { - if (options[i].selected) { values.push(options[i].value); } - } - return values; - } - - if (this.type === 'checkbox') { - return this.checked; - } - - return this.value.replace(/^\s+|\s+$/g, ''); - }, - - _trav: function(el, selector, fce) { - selector = selector.split('.'); - while (el && !(el.nodeType === 1 && - (!selector[0] || el.tagName.toLowerCase() === selector[0]) && - (!selector[1] || fn.hasClass.call(el, selector[1])))) { - el = el[fce]; - } - return $(el); - }, - - closest: function(selector) { - return fn._trav(this, selector, 'parentNode'); - }, - - prev: function(selector) { - return fn._trav(this.previousSibling, selector, 'previousSibling'); - }, - - next: function(selector) { - return fn._trav(this.nextSibling, selector, 'nextSibling'); - }, - - // returns total offset for element - offset: function(coords) { - var el = this, ofs = coords ? {left: -coords.left || 0, top: -coords.top || 0} : fn.position.call(el); - while (el = el.offsetParent) { ofs.left += el.offsetLeft; ofs.top += el.offsetTop; } - - if (coords) { - fn.position.call(this, {left: -ofs.left, top: -ofs.top}); - } else { - return ofs; - } - }, - - // returns current position or move to new position - position: function(coords) { - if (coords) { - if (this.nette && this.nette.onmove) { - this.nette.onmove.call(this, coords); - } - this.style.left = (coords.left || 0) + 'px'; - this.style.top = (coords.top || 0) + 'px'; - } else { - return {left: this.offsetLeft, top: this.offsetTop, width: this.offsetWidth, height: this.offsetHeight}; - } - }, - - // makes element draggable - draggable: function(options) { - var $el = $(this), dE = document.documentElement, started; - options = options || {}; - - $(options.handle || this).bind('mousedown', function(e) { - e.preventDefault(); - e.stopPropagation(); - - if (fn.draggable.binded) { // missed mouseup out of window? - return dE.onmouseup(e); - } - - var deltaX = $el[0].offsetLeft - e.clientX, deltaY = $el[0].offsetTop - e.clientY; - fn.draggable.binded = true; - started = false; - - dE.onmousemove = function(e) { - e = e || event; - if (!started) { - if (options.draggedClass) { - $el.addClass(options.draggedClass); - } - if (options.start) { - options.start(e, $el); - } - started = true; - } - $el.position({left: e.clientX + deltaX, top: e.clientY + deltaY}); - return false; - }; - - dE.onmouseup = function(e) { - if (started) { - if (options.draggedClass) { - $el.removeClass(options.draggedClass); - } - if (options.stop) { - options.stop(e || event, $el); - } - } - fn.draggable.binded = dE.onmousemove = dE.onmouseup = null; - return false; - }; - - }).bind('click', function(e) { - if (started) { - e.stopImmediatePropagation(); - preventClick = false; - } - }); - } -}); - -})(); +/** + * NetteQ + * + * This file is part of the Nette Framework. + * Copyright (c) 2004, 2012 David Grudl (http://davidgrudl.com) + */ + +var Nette = Nette || {}; + +(function(){ + +// simple class builder +Nette.Class = function(def) { + var cl = def.constructor || function(){}, nm, __hasProp = Object.prototype.hasOwnProperty; + delete def.constructor; + + if (def.Extends) { + var foo = function() { this.constructor = cl; }; + foo.prototype = def.Extends.prototype; + cl.prototype = new foo(); + delete def.Extends; + } + + if (def.Static) { + for (nm in def.Static) { if (__hasProp.call(def.Static, nm)) cl[nm] = def.Static[nm]; } + delete def.Static; + } + + for (nm in def) { if (__hasProp.call(def, nm)) cl.prototype[nm] = def[nm]; } + return cl; +}; + + +// supported cross-browser selectors: #id | div | div.class | .class +Nette.Q = Nette.Class({ + + Static: { + factory: function(selector) { + return new Nette.Q(selector); + }, + + implement: function(methods) { + var nm, fn = Nette.Q.implement, prot = Nette.Q.prototype, __hasProp = Object.prototype.hasOwnProperty; + for (nm in methods) { + if (!__hasProp.call(methods, nm)) { + continue; + } + fn[nm] = methods[nm]; + prot[nm] = (function(nm){ + return function() { return this.each(fn[nm], arguments); }; + }(nm)); + } + } + }, + + constructor: function(selector) { + if (typeof selector === "string") { + selector = this._find(document, selector); + + } else if (!selector || selector.nodeType || selector.length === undefined || selector === window) { + selector = [selector]; + } + + for (var i = 0, len = selector.length; i < len; i++) { + if (selector[i]) { this[this.length++] = selector[i]; } + } + }, + + length: 0, + + find: function(selector) { + return new Nette.Q(this._find(this[0], selector)); + }, + + _find: function(context, selector) { + if (!context || !selector) { + return []; + + } else if (document.querySelectorAll) { + return context.querySelectorAll(selector); + + } else if (selector.charAt(0) === '#') { // #id + return [document.getElementById(selector.substring(1))]; + + } else { // div | div.class | .class + selector = selector.split('.'); + var elms = context.getElementsByTagName(selector[0] || '*'); + + if (selector[1]) { + var list = [], pattern = new RegExp('(^|\\s)' + selector[1] + '(\\s|$)'); + for (var i = 0, len = elms.length; i < len; i++) { + if (pattern.test(elms[i].className)) { list.push(elms[i]); } + } + return list; + } else { + return elms; + } + } + }, + + dom: function() { + return this[0]; + }, + + each: function(callback, args) { + for (var i = 0, res; i < this.length; i++) { + if ((res = callback.apply(this[i], args || [])) !== undefined) { return res; } + } + return this; + } +}); + + +var $ = Nette.Q.factory, fn = Nette.Q.implement; + +fn({ + // cross-browser event attach + bind: function(event, handler) { + if (document.addEventListener && (event === 'mouseenter' || event === 'mouseleave')) { // simulate mouseenter & mouseleave using mouseover & mouseout + var old = handler; + event = event === 'mouseenter' ? 'mouseover' : 'mouseout'; + handler = function(e) { + for (var target = e.relatedTarget; target; target = target.parentNode) { + if (target === this) { return; } // target must not be inside this + } + old.call(this, e); + }; + } + + var data = fn.data.call(this), + events = data.events = data.events || {}; // use own handler queue + + if (!events[event]) { + var el = this, // fixes 'this' in iE + handlers = events[event] = [], + generic = fn.bind.genericHandler = function(e) { // dont worry, 'e' is passed in IE + if (!e.target) { + e.target = e.srcElement; + } + if (!e.preventDefault) { + e.preventDefault = function() { e.returnValue = false; }; + } + if (!e.stopPropagation) { + e.stopPropagation = function() { e.cancelBubble = true; }; + } + e.stopImmediatePropagation = function() { this.stopPropagation(); i = handlers.length; }; + for (var i = 0; i < handlers.length; i++) { + handlers[i].call(el, e); + } + }; + + if (document.addEventListener) { // non-IE + this.addEventListener(event, generic, false); + } else if (document.attachEvent) { // IE < 9 + this.attachEvent('on' + event, generic); + } + } + + events[event].push(handler); + }, + + // adds class to element + addClass: function(className) { + this.className = this.className.replace(/^|\s+|$/g, ' ').replace(' '+className+' ', ' ') + ' ' + className; + }, + + // removes class from element + removeClass: function(className) { + this.className = this.className.replace(/^|\s+|$/g, ' ').replace(' '+className+' ', ' '); + }, + + // tests whether element has given class + hasClass: function(className) { + return this.className.replace(/^|\s+|$/g, ' ').indexOf(' '+className+' ') > -1; + }, + + show: function() { + var dsp = fn.show.display = fn.show.display || {}, tag = this.tagName; + if (!dsp[tag]) { + var el = document.body.appendChild(document.createElement(tag)); + dsp[tag] = fn.css.call(el, 'display'); + } + this.style.display = dsp[tag]; + }, + + hide: function() { + this.style.display = 'none'; + }, + + css: function(property) { + return this.currentStyle ? this.currentStyle[property] + : (window.getComputedStyle ? document.defaultView.getComputedStyle(this, null).getPropertyValue(property) : undefined); + }, + + data: function() { + return this.nette ? this.nette : this.nette = {}; + }, + + val: function() { + var i; + if (!this.nodeName) { // radio + for (i = 0, len = this.length; i < len; i++) { + if (this[i].checked) { return this[i].value; } + } + return null; + } + + if (this.nodeName.toLowerCase() === 'select') { + var index = this.selectedIndex, options = this.options; + + if (index < 0) { + return null; + + } else if (this.type === 'select-one') { + return options[index].value; + } + + for (i = 0, values = [], len = options.length; i < len; i++) { + if (options[i].selected) { values.push(options[i].value); } + } + return values; + } + + if (this.type === 'checkbox') { + return this.checked; + } + + return this.value.replace(/^\s+|\s+$/g, ''); + }, + + _trav: function(el, selector, fce) { + selector = selector.split('.'); + while (el && !(el.nodeType === 1 && + (!selector[0] || el.tagName.toLowerCase() === selector[0]) && + (!selector[1] || fn.hasClass.call(el, selector[1])))) { + el = el[fce]; + } + return $(el); + }, + + closest: function(selector) { + return fn._trav(this, selector, 'parentNode'); + }, + + prev: function(selector) { + return fn._trav(this.previousSibling, selector, 'previousSibling'); + }, + + next: function(selector) { + return fn._trav(this.nextSibling, selector, 'nextSibling'); + }, + + // returns total offset for element + offset: function(coords) { + var el = this, ofs = coords ? {left: -coords.left || 0, top: -coords.top || 0} : fn.position.call(el); + while (el = el.offsetParent) { ofs.left += el.offsetLeft; ofs.top += el.offsetTop; } + + if (coords) { + fn.position.call(this, {left: -ofs.left, top: -ofs.top}); + } else { + return ofs; + } + }, + + // returns current position or move to new position + position: function(coords) { + if (coords) { + if (this.nette && this.nette.onmove) { + this.nette.onmove.call(this, coords); + } + this.style.left = (coords.left || 0) + 'px'; + this.style.top = (coords.top || 0) + 'px'; + } else { + return {left: this.offsetLeft, top: this.offsetTop, width: this.offsetWidth, height: this.offsetHeight}; + } + }, + + // makes element draggable + draggable: function(options) { + var $el = $(this), dE = document.documentElement, started; + options = options || {}; + + $(options.handle || this).bind('mousedown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + if (fn.draggable.binded) { // missed mouseup out of window? + return dE.onmouseup(e); + } + + var deltaX = $el[0].offsetLeft - e.clientX, deltaY = $el[0].offsetTop - e.clientY; + fn.draggable.binded = true; + started = false; + + dE.onmousemove = function(e) { + e = e || event; + if (!started) { + if (options.draggedClass) { + $el.addClass(options.draggedClass); + } + if (options.start) { + options.start(e, $el); + } + started = true; + } + $el.position({left: e.clientX + deltaX, top: e.clientY + deltaY}); + return false; + }; + + dE.onmouseup = function(e) { + if (started) { + if (options.draggedClass) { + $el.removeClass(options.draggedClass); + } + if (options.stop) { + options.stop(e || event, $el); + } + } + fn.draggable.binded = dE.onmousemove = dE.onmouseup = null; + return false; + }; + + }).bind('click', function(e) { + if (started) { + e.stopImmediatePropagation(); + preventClick = false; + } + }); + } +}); + +})(); diff --git a/libs/Nette/Forms/Container.php b/libs/Nette/Forms/Container.php index 14a910b..fdcef12 100644 --- a/libs/Nette/Forms/Container.php +++ b/libs/Nette/Forms/Container.php @@ -1,478 +1,492 @@ -getForm(FALSE); - if (!$form || !$form->isAnchored() || !$form->isSubmitted()) { - $this->setValues($values, $erase); - } - return $this; - } - - - - /** - * Fill-in with values. - * @param array|Traversable values used to fill the form - * @param bool erase other controls? - * @return Container provides a fluent interface - */ - public function setValues($values, $erase = FALSE) - { - if ($values instanceof \Traversable) { - $values = iterator_to_array($values); - - } elseif (!is_array($values)) { - throw new Nette\InvalidArgumentException("First parameter must be an array, " . gettype($values) ." given."); - } - - foreach ($this->getComponents() as $name => $control) { - if ($control instanceof IControl) { - if (array_key_exists($name, $values)) { - $control->setValue($values[$name]); - - } elseif ($erase) { - $control->setValue(NULL); - } - - } elseif ($control instanceof Container) { - if (array_key_exists($name, $values)) { - $control->setValues($values[$name], $erase); - - } elseif ($erase) { - $control->setValues(array(), $erase); - } - } - } - return $this; - } - - - - /** - * Returns the values submitted by the form. - * @return Nette\ArrayHash - */ - public function getValues() - { - $values = new Nette\ArrayHash; - foreach ($this->getComponents() as $name => $control) { - if ($control instanceof IControl && !$control->isDisabled() && !$control instanceof ISubmitterControl) { - $values->$name = $control->getValue(); - - } elseif ($control instanceof Container) { - $values->$name = $control->getValues(); - } - } - return $values; - } - - - - /********************* validation ****************d*g**/ - - - - /** - * Is form valid? - * @return bool - */ - public function isValid() - { - if ($this->valid === NULL) { - $this->validate(); - } - return $this->valid; - } - - - - /** - * Performs the server side validation. - * @return void - */ - public function validate() - { - $this->valid = TRUE; - $this->onValidate($this); - foreach ($this->getControls() as $control) { - if (!$control->getRules()->validate()) { - $this->valid = FALSE; - } - } - } - - - - /********************* form building ****************d*g**/ - - - - /** - * @param ControlGroup - * @return Container provides a fluent interface - */ - public function setCurrentGroup(ControlGroup $group = NULL) - { - $this->currentGroup = $group; - return $this; - } - - - - /** - * Returns current group. - * @return ControlGroup - */ - public function getCurrentGroup() - { - return $this->currentGroup; - } - - - - /** - * Adds the specified component to the IComponentContainer. - * @param IComponent - * @param string - * @param string - * @return void - * @throws Nette\InvalidStateException - */ - public function addComponent(Nette\ComponentModel\IComponent $component, $name, $insertBefore = NULL) - { - parent::addComponent($component, $name, $insertBefore); - if ($this->currentGroup !== NULL && $component instanceof IControl) { - $this->currentGroup->add($component); - } - } - - - - /** - * Iterates over all form controls. - * @return \ArrayIterator - */ - public function getControls() - { - return $this->getComponents(TRUE, 'Nette\Forms\IControl'); - } - - - - /** - * Returns form. - * @param bool throw exception if form doesn't exist? - * @return Form - */ - public function getForm($need = TRUE) - { - return $this->lookup('Nette\Forms\Form', $need); - } - - - - /********************* control factories ****************d*g**/ - - - - /** - * Adds single-line text input control to the form. - * @param string control name - * @param string label - * @param int width of the control - * @param int maximum number of characters the user may enter - * @return Nette\Forms\Controls\TextInput - */ - public function addText($name, $label = NULL, $cols = NULL, $maxLength = NULL) - { - return $this[$name] = new Controls\TextInput($label, $cols, $maxLength); - } - - - - /** - * Adds single-line text input control used for sensitive input such as passwords. - * @param string control name - * @param string label - * @param int width of the control - * @param int maximum number of characters the user may enter - * @return Nette\Forms\Controls\TextInput - */ - public function addPassword($name, $label = NULL, $cols = NULL, $maxLength = NULL) - { - $control = new Controls\TextInput($label, $cols, $maxLength); - $control->setType('password'); - return $this[$name] = $control; - } - - - - /** - * Adds multi-line text input control to the form. - * @param string control name - * @param string label - * @param int width of the control - * @param int height of the control in text lines - * @return Nette\Forms\Controls\TextArea - */ - public function addTextArea($name, $label = NULL, $cols = 40, $rows = 10) - { - return $this[$name] = new Controls\TextArea($label, $cols, $rows); - } - - - - /** - * Adds control that allows the user to upload files. - * @param string control name - * @param string label - * @return Nette\Forms\Controls\UploadControl - */ - public function addFile($name, $label = NULL) - { - return $this[$name] = new Controls\UploadControl($label); - } - - - - /** - * Adds hidden form control used to store a non-displayed value. - * @param string control name - * @param mixed default value - * @return Nette\Forms\Controls\HiddenField - */ - public function addHidden($name, $default = NULL) - { - $control = new Controls\HiddenField; - $control->setDefaultValue($default); - return $this[$name] = $control; - } - - - - /** - * Adds check box control to the form. - * @param string control name - * @param string caption - * @return Nette\Forms\Controls\Checkbox - */ - public function addCheckbox($name, $caption = NULL) - { - return $this[$name] = new Controls\Checkbox($caption); - } - - - - /** - * Adds set of radio button controls to the form. - * @param string control name - * @param string label - * @param array options from which to choose - * @return Nette\Forms\Controls\RadioList - */ - public function addRadioList($name, $label = NULL, array $items = NULL) - { - return $this[$name] = new Controls\RadioList($label, $items); - } - - - - /** - * Adds select box control that allows single item selection. - * @param string control name - * @param string label - * @param array items from which to choose - * @param int number of rows that should be visible - * @return Nette\Forms\Controls\SelectBox - */ - public function addSelect($name, $label = NULL, array $items = NULL, $size = NULL) - { - return $this[$name] = new Controls\SelectBox($label, $items, $size); - } - - - - /** - * Adds select box control that allows multiple item selection. - * @param string control name - * @param string label - * @param array options from which to choose - * @param int number of rows that should be visible - * @return Nette\Forms\Controls\MultiSelectBox - */ - public function addMultiSelect($name, $label = NULL, array $items = NULL, $size = NULL) - { - return $this[$name] = new Controls\MultiSelectBox($label, $items, $size); - } - - - - /** - * Adds button used to submit form. - * @param string control name - * @param string caption - * @return Nette\Forms\Controls\SubmitButton - */ - public function addSubmit($name, $caption = NULL) - { - return $this[$name] = new Controls\SubmitButton($caption); - } - - - - /** - * Adds push buttons with no default behavior. - * @param string control name - * @param string caption - * @return Nette\Forms\Controls\Button - */ - public function addButton($name, $caption) - { - return $this[$name] = new Controls\Button($caption); - } - - - - /** - * Adds graphical button used to submit form. - * @param string control name - * @param string URI of the image - * @param string alternate text for the image - * @return Nette\Forms\Controls\ImageButton - */ - public function addImage($name, $src = NULL, $alt = NULL) - { - return $this[$name] = new Controls\ImageButton($src, $alt); - } - - - - /** - * Adds naming container to the form. - * @param string name - * @return Container - */ - public function addContainer($name) - { - $control = new Container; - $control->currentGroup = $this->currentGroup; - return $this[$name] = $control; - } - - - - /********************* interface \ArrayAccess ****************d*g**/ - - - - /** - * Adds the component to the container. - * @param string component name - * @param Nette\ComponentModel\IComponent - * @return void - */ - final public function offsetSet($name, $component) - { - $this->addComponent($component, $name); - } - - - - /** - * Returns component specified by name. Throws exception if component doesn't exist. - * @param string component name - * @return Nette\ComponentModel\IComponent - * @throws Nette\InvalidArgumentException - */ - final public function offsetGet($name) - { - return $this->getComponent($name, TRUE); - } - - - - /** - * Does component specified by name exists? - * @param string component name - * @return bool - */ - final public function offsetExists($name) - { - return $this->getComponent($name, FALSE) !== NULL; - } - - - - /** - * Removes component from the container. - * @param string component name - * @return void - */ - final public function offsetUnset($name) - { - $component = $this->getComponent($name, FALSE); - if ($component !== NULL) { - $this->removeComponent($component); - } - } - - - - /** - * Prevents cloning. - */ - final public function __clone() - { - throw new Nette\NotImplementedException('Form cloning is not supported yet.'); - } - -} +getForm(FALSE); + if (!$form || !$form->isAnchored() || !$form->isSubmitted()) { + $this->setValues($values, $erase); + } + return $this; + } + + + + /** + * Fill-in with values. + * @param array|Traversable values used to fill the form + * @param bool erase other controls? + * @return Container provides a fluent interface + */ + public function setValues($values, $erase = FALSE) + { + if ($values instanceof \Traversable) { + $values = iterator_to_array($values); + + } elseif (!is_array($values)) { + throw new Nette\InvalidArgumentException("First parameter must be an array, " . gettype($values) ." given."); + } + + foreach ($this->getComponents() as $name => $control) { + if ($control instanceof IControl) { + if (array_key_exists($name, $values)) { + $control->setValue($values[$name]); + + } elseif ($erase) { + $control->setValue(NULL); + } + + } elseif ($control instanceof Container) { + if (array_key_exists($name, $values)) { + $control->setValues($values[$name], $erase); + + } elseif ($erase) { + $control->setValues(array(), $erase); + } + } + } + return $this; + } + + + + /** + * Returns the values submitted by the form. + * @param bool return values as an array? + * @return Nette\ArrayHash|array + */ + public function getValues($asArray = FALSE) + { + $values = $asArray ? array() : new Nette\ArrayHash; + foreach ($this->getComponents() as $name => $control) { + if ($control instanceof IControl && !$control->isDisabled() && !$control instanceof ISubmitterControl) { + $values[$name] = $control->getValue(); + + } elseif ($control instanceof Container) { + $values[$name] = $control->getValues($asArray); + } + } + return $values; + } + + + + /********************* validation ****************d*g**/ + + + + /** + * Is form valid? + * @return bool + */ + public function isValid() + { + if ($this->valid === NULL) { + $this->validate(); + } + return $this->valid; + } + + + + /** + * Performs the server side validation. + * @return void + */ + public function validate() + { + $this->valid = TRUE; + $this->onValidate($this); + foreach ($this->getControls() as $control) { + if (!$control->getRules()->validate()) { + $this->valid = FALSE; + } + } + } + + + + /********************* form building ****************d*g**/ + + + + /** + * @return Container provides a fluent interface + */ + public function setCurrentGroup(ControlGroup $group = NULL) + { + $this->currentGroup = $group; + return $this; + } + + + + /** + * Returns current group. + * @return ControlGroup + */ + public function getCurrentGroup() + { + return $this->currentGroup; + } + + + + /** + * Adds the specified component to the IContainer. + * @param IComponent + * @param string + * @param string + * @return Container provides a fluent interface + * @throws Nette\InvalidStateException + */ + public function addComponent(Nette\ComponentModel\IComponent $component, $name, $insertBefore = NULL) + { + parent::addComponent($component, $name, $insertBefore); + if ($this->currentGroup !== NULL && $component instanceof IControl) { + $this->currentGroup->add($component); + } + return $this; + } + + + + /** + * Iterates over all form controls. + * @return \ArrayIterator + */ + public function getControls() + { + return $this->getComponents(TRUE, 'Nette\Forms\IControl'); + } + + + + /** + * Returns form. + * @param bool throw exception if form doesn't exist? + * @return Form + */ + public function getForm($need = TRUE) + { + return $this->lookup('Nette\Forms\Form', $need); + } + + + + /********************* control factories ****************d*g**/ + + + + /** + * Adds single-line text input control to the form. + * @param string control name + * @param string label + * @param int width of the control + * @param int maximum number of characters the user may enter + * @return Nette\Forms\Controls\TextInput + */ + public function addText($name, $label = NULL, $cols = NULL, $maxLength = NULL) + { + return $this[$name] = new Controls\TextInput($label, $cols, $maxLength); + } + + + + /** + * Adds single-line text input control used for sensitive input such as passwords. + * @param string control name + * @param string label + * @param int width of the control + * @param int maximum number of characters the user may enter + * @return Nette\Forms\Controls\TextInput + */ + public function addPassword($name, $label = NULL, $cols = NULL, $maxLength = NULL) + { + $control = new Controls\TextInput($label, $cols, $maxLength); + $control->setType('password'); + return $this[$name] = $control; + } + + + + /** + * Adds multi-line text input control to the form. + * @param string control name + * @param string label + * @param int width of the control + * @param int height of the control in text lines + * @return Nette\Forms\Controls\TextArea + */ + public function addTextArea($name, $label = NULL, $cols = 40, $rows = 10) + { + return $this[$name] = new Controls\TextArea($label, $cols, $rows); + } + + + + /** + * Adds control that allows the user to upload files. + * @param string control name + * @param string label + * @return Nette\Forms\Controls\UploadControl + */ + public function addUpload($name, $label = NULL) + { + return $this[$name] = new Controls\UploadControl($label); + } + + + + /** + * Adds hidden form control used to store a non-displayed value. + * @param string control name + * @param mixed default value + * @return Nette\Forms\Controls\HiddenField + */ + public function addHidden($name, $default = NULL) + { + $control = new Controls\HiddenField; + $control->setDefaultValue($default); + return $this[$name] = $control; + } + + + + /** + * Adds check box control to the form. + * @param string control name + * @param string caption + * @return Nette\Forms\Controls\Checkbox + */ + public function addCheckbox($name, $caption = NULL) + { + return $this[$name] = new Controls\Checkbox($caption); + } + + + + /** + * Adds set of radio button controls to the form. + * @param string control name + * @param string label + * @param array options from which to choose + * @return Nette\Forms\Controls\RadioList + */ + public function addRadioList($name, $label = NULL, array $items = NULL) + { + return $this[$name] = new Controls\RadioList($label, $items); + } + + + + /** + * Adds select box control that allows single item selection. + * @param string control name + * @param string label + * @param array items from which to choose + * @param int number of rows that should be visible + * @return Nette\Forms\Controls\SelectBox + */ + public function addSelect($name, $label = NULL, array $items = NULL, $size = NULL) + { + return $this[$name] = new Controls\SelectBox($label, $items, $size); + } + + + + /** + * Adds select box control that allows multiple item selection. + * @param string control name + * @param string label + * @param array options from which to choose + * @param int number of rows that should be visible + * @return Nette\Forms\Controls\MultiSelectBox + */ + public function addMultiSelect($name, $label = NULL, array $items = NULL, $size = NULL) + { + return $this[$name] = new Controls\MultiSelectBox($label, $items, $size); + } + + + + /** + * Adds button used to submit form. + * @param string control name + * @param string caption + * @return Nette\Forms\Controls\SubmitButton + */ + public function addSubmit($name, $caption = NULL) + { + return $this[$name] = new Controls\SubmitButton($caption); + } + + + + /** + * Adds push buttons with no default behavior. + * @param string control name + * @param string caption + * @return Nette\Forms\Controls\Button + */ + public function addButton($name, $caption) + { + return $this[$name] = new Controls\Button($caption); + } + + + + /** + * Adds graphical button used to submit form. + * @param string control name + * @param string URI of the image + * @param string alternate text for the image + * @return Nette\Forms\Controls\ImageButton + */ + public function addImage($name, $src = NULL, $alt = NULL) + { + return $this[$name] = new Controls\ImageButton($src, $alt); + } + + + + /** + * Adds naming container to the form. + * @param string name + * @return Container + */ + public function addContainer($name) + { + $control = new Container; + $control->currentGroup = $this->currentGroup; + return $this[$name] = $control; + } + + + + /********************* interface \ArrayAccess ****************d*g**/ + + + + /** + * Adds the component to the container. + * @param string component name + * @param Nette\ComponentModel\IComponent + * @return void + */ + final public function offsetSet($name, $component) + { + $this->addComponent($component, $name); + } + + + + /** + * Returns component specified by name. Throws exception if component doesn't exist. + * @param string component name + * @return Nette\ComponentModel\IComponent + * @throws Nette\InvalidArgumentException + */ + final public function offsetGet($name) + { + return $this->getComponent($name, TRUE); + } + + + + /** + * Does component specified by name exists? + * @param string component name + * @return bool + */ + final public function offsetExists($name) + { + return $this->getComponent($name, FALSE) !== NULL; + } + + + + /** + * Removes component from the container. + * @param string component name + * @return void + */ + final public function offsetUnset($name) + { + $component = $this->getComponent($name, FALSE); + if ($component !== NULL) { + $this->removeComponent($component); + } + } + + + + /** + * Prevents cloning. + */ + final public function __clone() + { + throw new Nette\NotImplementedException('Form cloning is not supported yet.'); + } + + + + /********************* deprecated ****************d*g**/ + + /** @deprecated */ + function addFile($name, $label = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use addUpload() instead.', E_USER_WARNING); + return $this->addUpload($name, $label); + } + +} diff --git a/libs/Nette/Forms/ControlGroup.php b/libs/Nette/Forms/ControlGroup.php index ad717d3..730d846 100644 --- a/libs/Nette/Forms/ControlGroup.php +++ b/libs/Nette/Forms/ControlGroup.php @@ -1,124 +1,124 @@ -controls = new \SplObjectStorage; - } - - - - /** - * @return ControlGroup provides a fluent interface - */ - public function add() - { - foreach (func_get_args() as $num => $item) { - if ($item instanceof IControl) { - $this->controls->attach($item); - - } elseif ($item instanceof \Traversable || is_array($item)) { - foreach ($item as $control) { - $this->controls->attach($control); - } - - } else { - throw new Nette\InvalidArgumentException("Only IFormControl items are allowed, the #$num parameter is invalid."); - } - } - return $this; - } - - - - /** - * @return array IFormControl - */ - public function getControls() - { - return iterator_to_array($this->controls); - } - - - - /** - * Sets user-specific option. - * Options recognized by DefaultFormRenderer - * - 'label' - textual or Html object label - * - 'visual' - indicates visual group - * - 'container' - container as Html object - * - 'description' - textual or Html object description - * - 'embedNext' - describes how render next group - * - * @param string key - * @param mixed value - * @return ControlGroup provides a fluent interface - */ - public function setOption($key, $value) - { - if ($value === NULL) { - unset($this->options[$key]); - - } else { - $this->options[$key] = $value; - } - return $this; - } - - - - /** - * Returns user-specific option. - * @param string key - * @param mixed default value - * @return mixed - */ - final public function getOption($key, $default = NULL) - { - return isset($this->options[$key]) ? $this->options[$key] : $default; - } - - - - /** - * Returns user-specific options. - * @return array - */ - final public function getOptions() - { - return $this->options; - } - -} +controls = new \SplObjectStorage; + } + + + + /** + * @return ControlGroup provides a fluent interface + */ + public function add() + { + foreach (func_get_args() as $num => $item) { + if ($item instanceof IControl) { + $this->controls->attach($item); + + } elseif ($item instanceof \Traversable || is_array($item)) { + foreach ($item as $control) { + $this->controls->attach($control); + } + + } else { + throw new Nette\InvalidArgumentException("Only IFormControl items are allowed, the #$num parameter is invalid."); + } + } + return $this; + } + + + + /** + * @return array IFormControl + */ + public function getControls() + { + return iterator_to_array($this->controls); + } + + + + /** + * Sets user-specific option. + * Options recognized by DefaultFormRenderer + * - 'label' - textual or Html object label + * - 'visual' - indicates visual group + * - 'container' - container as Html object + * - 'description' - textual or Html object description + * - 'embedNext' - describes how render next group + * + * @param string key + * @param mixed value + * @return ControlGroup provides a fluent interface + */ + public function setOption($key, $value) + { + if ($value === NULL) { + unset($this->options[$key]); + + } else { + $this->options[$key] = $value; + } + return $this; + } + + + + /** + * Returns user-specific option. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getOption($key, $default = NULL) + { + return isset($this->options[$key]) ? $this->options[$key] : $default; + } + + + + /** + * Returns user-specific options. + * @return array + */ + final public function getOptions() + { + return $this->options; + } + +} diff --git a/libs/Nette/Forms/Controls/BaseControl.php b/libs/Nette/Forms/Controls/BaseControl.php index 2f24f73..52d6510 100644 --- a/libs/Nette/Forms/Controls/BaseControl.php +++ b/libs/Nette/Forms/Controls/BaseControl.php @@ -1,661 +1,664 @@ -monitor('Nette\Forms\Form'); - parent::__construct(); - $this->control = Html::el('input'); - $this->label = Html::el('label'); - $this->caption = $caption; - $this->rules = new Nette\Forms\Rules($this); - } - - - - /** - * This method will be called when the component becomes attached to Form. - * @param Nette\Forms\IComponent - * @return void - */ - protected function attached($form) - { - if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) { - $this->htmlName = NULL; - $this->loadHttpData(); - } - } - - - - /** - * Returns form. - * @param bool throw exception if form doesn't exist? - * @return Nette\Forms\Form - */ - public function getForm($need = TRUE) - { - return $this->lookup('Nette\Forms\Form', $need); - } - - - - /** - * Returns HTML name of control. - * @return string - */ - public function getHtmlName() - { - if ($this->htmlName === NULL) { - $name = str_replace(self::NAME_SEPARATOR, '][', $this->lookupPath('Nette\Forms\Form'), $count); - if ($count) { - $name = substr_replace($name, '', strpos($name, ']'), 1) . ']'; - } - if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) { - $name .= '_'; - } - $this->htmlName = $name; - } - return $this->htmlName; - } - - - - /** - * Changes control's HTML id. - * @param string new ID, or FALSE or NULL - * @return BaseControl provides a fluent interface - */ - public function setHtmlId($id) - { - $this->htmlId = $id; - return $this; - } - - - - /** - * Returns control's HTML id. - * @return string - */ - public function getHtmlId() - { - if ($this->htmlId === FALSE) { - return NULL; - - } elseif ($this->htmlId === NULL) { - $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->lookupPath('Nette\Forms\Form')); - } - return $this->htmlId; - } - - - - /** - * Changes control's HTML attribute. - * @param string name - * @param mixed value - * @return BaseControl provides a fluent interface - */ - public function setAttribute($name, $value = TRUE) - { - $this->control->$name = $value; - return $this; - } - - - - /** - * Sets user-specific option. - * @param string key - * @param mixed value - * @return BaseControl provides a fluent interface - */ - public function setOption($key, $value) - { - if ($value === NULL) { - unset($this->options[$key]); - - } else { - $this->options[$key] = $value; - } - return $this; - } - - - - /** - * Returns user-specific option. - * @param string key - * @param mixed default value - * @return mixed - */ - final public function getOption($key, $default = NULL) - { - return isset($this->options[$key]) ? $this->options[$key] : $default; - } - - - - /** - * Returns user-specific options. - * @return array - */ - final public function getOptions() - { - return $this->options; - } - - - - /********************* translator ****************d*g**/ - - - - /** - * Sets translate adapter. - * @param Nette\Localization\ITranslator - * @return BaseControl provides a fluent interface - */ - public function setTranslator(Nette\Localization\ITranslator $translator = NULL) - { - $this->translator = $translator; - return $this; - } - - - - /** - * Returns translate adapter. - * @return Nette\Localization\ITranslator|NULL - */ - final public function getTranslator() - { - if ($this->translator === TRUE) { - return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL; - } - return $this->translator; - } - - - - /** - * Returns translated string. - * @param string - * @param int plural count - * @return string - */ - public function translate($s, $count = NULL) - { - $translator = $this->getTranslator(); - return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); // intentionally == - } - - - - /********************* interface IFormControl ****************d*g**/ - - - - /** - * Sets control's value. - * @param mixed - * @return BaseControl provides a fluent interface - */ - public function setValue($value) - { - $this->value = $value; - return $this; - } - - - - /** - * Returns control's value. - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - - - /** - * Is control filled? - * @return bool - */ - public function isFilled() - { - return (string) $this->getValue() !== ''; // NULL, FALSE, '' ==> FALSE - } - - - - /** - * Sets control's default value. - * @param mixed - * @return BaseControl provides a fluent interface - */ - public function setDefaultValue($value) - { - $form = $this->getForm(FALSE); - if (!$form || !$form->isAnchored() || !$form->isSubmitted()) { - $this->setValue($value); - } - return $this; - } - - - - /** - * Loads HTTP data. - * @return void - */ - public function loadHttpData() - { - $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_')); - $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path)); - } - - - - /** - * Disables or enables control. - * @param bool - * @return BaseControl provides a fluent interface - */ - public function setDisabled($value = TRUE) - { - $this->disabled = (bool) $value; - return $this; - } - - - - /** - * Is control disabled? - * @return bool - */ - public function isDisabled() - { - return $this->disabled; - } - - - - /********************* rendering ****************d*g**/ - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - $this->setOption('rendered', TRUE); - - $control = clone $this->control; - $control->name = $this->getHtmlName(); - $control->disabled = $this->disabled; - $control->id = $this->getHtmlId(); - $control->required = $this->isRequired(); - - $rules = self::exportRules($this->rules); - $rules = substr(json_encode($rules), 1, -1); - $rules = preg_replace('#"([a-z0-9]+)":#i', '$1:', $rules); - $rules = preg_replace('#(?data('nette-rules', $rules ? $rules : NULL); - - return $control; - } - - - - /** - * Generates label's HTML element. - * @param string - * @return Nette\Utils\Html - */ - public function getLabel($caption = NULL) - { - $label = clone $this->label; - $label->for = $this->getHtmlId(); - if ($caption !== NULL) { - $label->setText($this->translate($caption)); - - } elseif ($this->caption instanceof Html) { - $label->add($this->caption); - - } else { - $label->setText($this->translate($this->caption)); - } - return $label; - } - - - - /** - * Returns control's HTML element template. - * @return Nette\Utils\Html - */ - final public function getControlPrototype() - { - return $this->control; - } - - - - /** - * Returns label's HTML element template. - * @return Nette\Utils\Html - */ - final public function getLabelPrototype() - { - return $this->label; - } - - - - /********************* rules ****************d*g**/ - - - - /** - * Adds a validation rule. - * @param mixed rule type - * @param string message to display for invalid data - * @param mixed optional rule arguments - * @return BaseControl provides a fluent interface - */ - public function addRule($operation, $message = NULL, $arg = NULL) - { - $this->rules->addRule($operation, $message, $arg); - return $this; - } - - - - /** - * Adds a validation condition a returns new branch. - * @param mixed condition type - * @param mixed optional condition arguments - * @return Nette\Forms\Rules new branch - */ - public function addCondition($operation, $value = NULL) - { - return $this->rules->addCondition($operation, $value); - } - - - - /** - * Adds a validation condition based on another control a returns new branch. - * @param Nette\Forms\IControl form control - * @param mixed condition type - * @param mixed optional condition arguments - * @return Nette\Forms\Rules new branch - */ - public function addConditionOn(IControl $control, $operation, $value = NULL) - { - return $this->rules->addConditionOn($control, $operation, $value); - } - - - - /** - * @return Nette\Forms\Rules - */ - final public function getRules() - { - return $this->rules; - } - - - - /** - * Makes control mandatory. - * @param string error message - * @return BaseControl provides a fluent interface - */ - final public function setRequired($message = NULL) - { - return $this->addRule(Form::FILLED, $message); - } - - - - /** - * Is control mandatory? - * @return bool - */ - final public function isRequired() - { - foreach ($this->rules as $rule) { - if ($rule->type === Rule::VALIDATOR && !$rule->isNegative && $rule->operation === Form::FILLED) { - return TRUE; - } - } - return FALSE; - } - - - - /** - * @return array - */ - private static function exportRules($rules) - { - $payload = array(); - foreach ($rules as $rule) { - if (!is_string($op = $rule->operation)) { - $op = callback($op); - if (!$op->isStatic()) { - continue; - } - } - if ($rule->type === Rule::VALIDATOR) { - $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE)); - - } elseif ($rule->type === Rule::CONDITION) { - $item = array( - 'op' => ($rule->isNegative ? '~' : '') . $op, - 'rules' => self::exportRules($rule->subRules), - 'control' => $rule->control->getHtmlName() - ); - if ($rule->subRules->getToggles()) { - $item['toggle'] = $rule->subRules->getToggles(); - } - } - - if (is_array($rule->arg)) { - foreach ($rule->arg as $key => $value) { - $item['arg'][$key] = $value instanceof IControl ? (object) array('control' => $value->getHtmlName()) : $value; - } - } elseif ($rule->arg !== NULL) { - $item['arg'] = $rule->arg instanceof IControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg; - } - - $payload[] = $item; - } - return $payload; - } - - - - /********************* validation ****************d*g**/ - - - - /** - * Equal validator: are control's value and second parameter equal? - * @param Nette\Forms\IControl - * @param mixed - * @return bool - */ - public static function validateEqual(IControl $control, $arg) - { - $value = $control->getValue(); - foreach ((is_array($value) ? $value : array($value)) as $val) { - foreach ((is_array($arg) ? $arg : array($arg)) as $item) { - if ((string) $val === (string) ($item instanceof IControl ? $item->value : $item)) { - return TRUE; - } - } - } - return FALSE; - } - - - - /** - * Filled validator: is control filled? - * @param Nette\Forms\IControl - * @return bool - */ - public static function validateFilled(IControl $control) - { - return $control->isFilled(); - } - - - - /** - * Valid validator: is control valid? - * @param Nette\Forms\IControl - * @return bool - */ - public static function validateValid(IControl $control) - { - return $control->rules->validate(TRUE); - } - - - - /** - * Adds error message to the list. - * @param string error message - * @return void - */ - public function addError($message) - { - if (!in_array($message, $this->errors, TRUE)) { - $this->errors[] = $message; - } - $this->getForm()->addError($message); - } - - - - /** - * Returns errors corresponding to control. - * @return array - */ - public function getErrors() - { - return $this->errors; - } - - - - /** - * @return bool - */ - public function hasErrors() - { - return (bool) $this->errors; - } - - - - /** - * @return void - */ - public function cleanErrors() - { - $this->errors = array(); - } - -} +monitor('Nette\Forms\Form'); + parent::__construct(); + $this->control = Html::el('input'); + $this->label = Html::el('label'); + $this->caption = $caption; + $this->rules = new Nette\Forms\Rules($this); + } + + + + /** + * This method will be called when the component becomes attached to Form. + * @param Nette\Forms\IComponent + * @return void + */ + protected function attached($form) + { + if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) { + $this->htmlName = NULL; + $this->loadHttpData(); + } + } + + + + /** + * Returns form. + * @param bool throw exception if form doesn't exist? + * @return Nette\Forms\Form + */ + public function getForm($need = TRUE) + { + return $this->lookup('Nette\Forms\Form', $need); + } + + + + /** + * Returns HTML name of control. + * @return string + */ + public function getHtmlName() + { + if ($this->htmlName === NULL) { + $name = str_replace(self::NAME_SEPARATOR, '][', $this->lookupPath('Nette\Forms\Form'), $count); + if ($count) { + $name = substr_replace($name, '', strpos($name, ']'), 1) . ']'; + } + if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) { + $name .= '_'; + } + $this->htmlName = $name; + } + return $this->htmlName; + } + + + + /** + * Changes control's HTML id. + * @param string new ID, or FALSE or NULL + * @return BaseControl provides a fluent interface + */ + public function setHtmlId($id) + { + $this->htmlId = $id; + return $this; + } + + + + /** + * Returns control's HTML id. + * @return string + */ + public function getHtmlId() + { + if ($this->htmlId === FALSE) { + return NULL; + + } elseif ($this->htmlId === NULL) { + $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->lookupPath('Nette\Forms\Form')); + } + return $this->htmlId; + } + + + + /** + * Changes control's HTML attribute. + * @param string name + * @param mixed value + * @return BaseControl provides a fluent interface + */ + public function setAttribute($name, $value = TRUE) + { + $this->control->$name = $value; + return $this; + } + + + + /** + * Sets user-specific option. + * Options recognized by DefaultFormRenderer + * - 'description' - textual or Html object description + * + * @param string key + * @param mixed value + * @return BaseControl provides a fluent interface + */ + public function setOption($key, $value) + { + if ($value === NULL) { + unset($this->options[$key]); + + } else { + $this->options[$key] = $value; + } + return $this; + } + + + + /** + * Returns user-specific option. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getOption($key, $default = NULL) + { + return isset($this->options[$key]) ? $this->options[$key] : $default; + } + + + + /** + * Returns user-specific options. + * @return array + */ + final public function getOptions() + { + return $this->options; + } + + + + /********************* translator ****************d*g**/ + + + + /** + * Sets translate adapter. + * @return BaseControl provides a fluent interface + */ + public function setTranslator(Nette\Localization\ITranslator $translator = NULL) + { + $this->translator = $translator; + return $this; + } + + + + /** + * Returns translate adapter. + * @return Nette\Localization\ITranslator|NULL + */ + final public function getTranslator() + { + if ($this->translator === TRUE) { + return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL; + } + return $this->translator; + } + + + + /** + * Returns translated string. + * @param string + * @param int plural count + * @return string + */ + public function translate($s, $count = NULL) + { + $translator = $this->getTranslator(); + return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); // intentionally == + } + + + + /********************* interface IFormControl ****************d*g**/ + + + + /** + * Sets control's value. + * @param mixed + * @return BaseControl provides a fluent interface + */ + public function setValue($value) + { + $this->value = $value; + return $this; + } + + + + /** + * Returns control's value. + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + + + /** + * Is control filled? + * @return bool + */ + public function isFilled() + { + return (string) $this->getValue() !== ''; // NULL, FALSE, '' ==> FALSE + } + + + + /** + * Sets control's default value. + * @param mixed + * @return BaseControl provides a fluent interface + */ + public function setDefaultValue($value) + { + $form = $this->getForm(FALSE); + if (!$form || !$form->isAnchored() || !$form->isSubmitted()) { + $this->setValue($value); + } + return $this; + } + + + + /** + * Loads HTTP data. + * @return void + */ + public function loadHttpData() + { + $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_')); + $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL)); + } + + + + /** + * Disables or enables control. + * @param bool + * @return BaseControl provides a fluent interface + */ + public function setDisabled($value = TRUE) + { + $this->disabled = (bool) $value; + return $this; + } + + + + /** + * Is control disabled? + * @return bool + */ + public function isDisabled() + { + return $this->disabled; + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $this->setOption('rendered', TRUE); + + $control = clone $this->control; + $control->name = $this->getHtmlName(); + $control->disabled = $this->disabled; + $control->id = $this->getHtmlId(); + $control->required = $this->isRequired(); + + $rules = self::exportRules($this->rules); + $rules = substr(PHP_VERSION_ID >= 50400 ? json_encode($rules, JSON_UNESCAPED_UNICODE) : json_encode($rules), 1, -1); + $rules = preg_replace('#"([a-z0-9_]+)":#i', '$1:', $rules); + $rules = preg_replace('#(?data('nette-rules', $rules ? $rules : NULL); + + return $control; + } + + + + /** + * Generates label's HTML element. + * @param string + * @return Nette\Utils\Html + */ + public function getLabel($caption = NULL) + { + $label = clone $this->label; + $label->for = $this->getHtmlId(); + if ($caption !== NULL) { + $label->setText($this->translate($caption)); + + } elseif ($this->caption instanceof Html) { + $label->add($this->caption); + + } else { + $label->setText($this->translate($this->caption)); + } + return $label; + } + + + + /** + * Returns control's HTML element template. + * @return Nette\Utils\Html + */ + final public function getControlPrototype() + { + return $this->control; + } + + + + /** + * Returns label's HTML element template. + * @return Nette\Utils\Html + */ + final public function getLabelPrototype() + { + return $this->label; + } + + + + /********************* rules ****************d*g**/ + + + + /** + * Adds a validation rule. + * @param mixed rule type + * @param string message to display for invalid data + * @param mixed optional rule arguments + * @return BaseControl provides a fluent interface + */ + public function addRule($operation, $message = NULL, $arg = NULL) + { + $this->rules->addRule($operation, $message, $arg); + return $this; + } + + + + /** + * Adds a validation condition a returns new branch. + * @param mixed condition type + * @param mixed optional condition arguments + * @return Nette\Forms\Rules new branch + */ + public function addCondition($operation, $value = NULL) + { + return $this->rules->addCondition($operation, $value); + } + + + + /** + * Adds a validation condition based on another control a returns new branch. + * @param Nette\Forms\IControl form control + * @param mixed condition type + * @param mixed optional condition arguments + * @return Nette\Forms\Rules new branch + */ + public function addConditionOn(IControl $control, $operation, $value = NULL) + { + return $this->rules->addConditionOn($control, $operation, $value); + } + + + + /** + * @return Nette\Forms\Rules + */ + final public function getRules() + { + return $this->rules; + } + + + + /** + * Makes control mandatory. + * @param string error message + * @return BaseControl provides a fluent interface + */ + final public function setRequired($message = NULL) + { + return $this->addRule(Form::FILLED, $message); + } + + + + /** + * Is control mandatory? + * @return bool + */ + final public function isRequired() + { + foreach ($this->rules as $rule) { + if ($rule->type === Rule::VALIDATOR && !$rule->isNegative && $rule->operation === Form::FILLED) { + return TRUE; + } + } + return FALSE; + } + + + + /** + * @return array + */ + protected static function exportRules($rules) + { + $payload = array(); + foreach ($rules as $rule) { + if (!is_string($op = $rule->operation)) { + $op = new Nette\Callback($op); + if (!$op->isStatic()) { + continue; + } + } + if ($rule->type === Rule::VALIDATOR) { + $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE)); + + } elseif ($rule->type === Rule::CONDITION) { + $item = array( + 'op' => ($rule->isNegative ? '~' : '') . $op, + 'rules' => self::exportRules($rule->subRules), + 'control' => $rule->control->getHtmlName() + ); + if ($rule->subRules->getToggles()) { + $item['toggle'] = $rule->subRules->getToggles(); + } + } + + if (is_array($rule->arg)) { + foreach ($rule->arg as $key => $value) { + $item['arg'][$key] = $value instanceof IControl ? (object) array('control' => $value->getHtmlName()) : $value; + } + } elseif ($rule->arg !== NULL) { + $item['arg'] = $rule->arg instanceof IControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg; + } + + $payload[] = $item; + } + return $payload; + } + + + + /********************* validation ****************d*g**/ + + + + /** + * Equal validator: are control's value and second parameter equal? + * @param Nette\Forms\IControl + * @param mixed + * @return bool + */ + public static function validateEqual(IControl $control, $arg) + { + $value = $control->getValue(); + foreach ((is_array($value) ? $value : array($value)) as $val) { + foreach ((is_array($arg) ? $arg : array($arg)) as $item) { + if ((string) $val === (string) ($item instanceof IControl ? $item->value : $item)) { + return TRUE; + } + } + } + return FALSE; + } + + + + /** + * Filled validator: is control filled? + * @param Nette\Forms\IControl + * @return bool + */ + public static function validateFilled(IControl $control) + { + return $control->isFilled(); + } + + + + /** + * Valid validator: is control valid? + * @return bool + */ + public static function validateValid(IControl $control) + { + return $control->rules->validate(TRUE); + } + + + + /** + * Adds error message to the list. + * @param string error message + * @return void + */ + public function addError($message) + { + if (!in_array($message, $this->errors, TRUE)) { + $this->errors[] = $message; + } + $this->getForm()->addError($message); + } + + + + /** + * Returns errors corresponding to control. + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + + + /** + * @return bool + */ + public function hasErrors() + { + return (bool) $this->errors; + } + + + + /** + * @return void + */ + public function cleanErrors() + { + $this->errors = array(); + } + +} diff --git a/libs/Nette/Forms/Controls/Button.php b/libs/Nette/Forms/Controls/Button.php index 8642b74..aed3b48 100644 --- a/libs/Nette/Forms/Controls/Button.php +++ b/libs/Nette/Forms/Controls/Button.php @@ -1,60 +1,60 @@ -control->type = 'button'; - } - - - - /** - * Bypasses label generation. - * @return void - */ - public function getLabel($caption = NULL) - { - return NULL; - } - - - - /** - * Generates control's HTML element. - * @param string - * @return Nette\Utils\Html - */ - public function getControl($caption = NULL) - { - $control = parent::getControl(); - $control->value = $this->translate($caption === NULL ? $this->caption : $caption); - return $control; - } - -} +control->type = 'button'; + } + + + + /** + * Bypasses label generation. + * @return void + */ + public function getLabel($caption = NULL) + { + return NULL; + } + + + + /** + * Generates control's HTML element. + * @param string + * @return Nette\Utils\Html + */ + public function getControl($caption = NULL) + { + $control = parent::getControl(); + $control->value = $this->translate($caption === NULL ? $this->caption : $caption); + return $control; + } + +} diff --git a/libs/Nette/Forms/Controls/Checkbox.php b/libs/Nette/Forms/Controls/Checkbox.php index d0af337..049bffd 100644 --- a/libs/Nette/Forms/Controls/Checkbox.php +++ b/libs/Nette/Forms/Controls/Checkbox.php @@ -1,60 +1,60 @@ -control->type = 'checkbox'; - $this->value = FALSE; - } - - - - /** - * Sets control's value. - * @param bool - * @return Checkbox provides a fluent interface - */ - public function setValue($value) - { - $this->value = is_scalar($value) ? (bool) $value : FALSE; - return $this; - } - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - return parent::getControl()->checked($this->value); - } - -} +control->type = 'checkbox'; + $this->value = FALSE; + } + + + + /** + * Sets control's value. + * @param bool + * @return Checkbox provides a fluent interface + */ + public function setValue($value) + { + $this->value = is_scalar($value) ? (bool) $value : FALSE; + return $this; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + return parent::getControl()->checked($this->value); + } + +} diff --git a/libs/Nette/Forms/Controls/HiddenField.php b/libs/Nette/Forms/Controls/HiddenField.php index f2ad3ca..8ba7f93 100644 --- a/libs/Nette/Forms/Controls/HiddenField.php +++ b/libs/Nette/Forms/Controls/HiddenField.php @@ -1,75 +1,75 @@ -control->type = 'hidden'; - $this->value = (string) $forcedValue; - $this->forcedValue = $forcedValue; - } - - - - /** - * Bypasses label generation. - * @return void - */ - public function getLabel($caption = NULL) - { - return NULL; - } - - - - /** - * Sets control's value. - * @param string - * @return HiddenField provides a fluent interface - */ - public function setValue($value) - { - $this->value = is_scalar($value) ? (string) $value : ''; - return $this; - } - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - return parent::getControl() - ->value($this->forcedValue === NULL ? $this->value : $this->forcedValue) - ->data('nette-rules', NULL); - } - -} +control->type = 'hidden'; + $this->value = (string) $forcedValue; + $this->forcedValue = $forcedValue; + } + + + + /** + * Bypasses label generation. + * @return void + */ + public function getLabel($caption = NULL) + { + return NULL; + } + + + + /** + * Sets control's value. + * @param string + * @return HiddenField provides a fluent interface + */ + public function setValue($value) + { + $this->value = is_scalar($value) ? (string) $value : ''; + return $this; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + return parent::getControl() + ->value($this->forcedValue === NULL ? $this->value : $this->forcedValue) + ->data('nette-rules', NULL); + } + +} diff --git a/libs/Nette/Forms/Controls/ImageButton.php b/libs/Nette/Forms/Controls/ImageButton.php index 3414431..2f93ee9 100644 --- a/libs/Nette/Forms/Controls/ImageButton.php +++ b/libs/Nette/Forms/Controls/ImageButton.php @@ -1,63 +1,63 @@ -control->type = 'image'; - $this->control->src = $src; - $this->control->alt = $alt; - } - - - - /** - * Returns HTML name of control. - * @return string - */ - public function getHtmlName() - { - $name = parent::getHtmlName(); - return strpos($name, '[') === FALSE ? $name : $name . '[]'; - } - - - - /** - * Loads HTTP data. - * @return void - */ - public function loadHttpData() - { - $path = $this->getHtmlName(); // img_x or img['x'] - $path = explode('[', strtr(str_replace(']', '', strpos($path, '[') === FALSE ? $path . '.x' : substr($path, 0, -2)), '.', '_')); - $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path) !== NULL); - } - -} +control->type = 'image'; + $this->control->src = $src; + $this->control->alt = $alt; + } + + + + /** + * Returns HTML name of control. + * @return string + */ + public function getHtmlName() + { + $name = parent::getHtmlName(); + return strpos($name, '[') === FALSE ? $name : $name . '[]'; + } + + + + /** + * Loads HTTP data. + * @return void + */ + public function loadHttpData() + { + $path = $this->getHtmlName(); // img_x or img['x'] + $path = explode('[', strtr(str_replace(']', '', strpos($path, '[') === FALSE ? $path . '.x' : substr($path, 0, -2)), '.', '_')); + $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL)); + } + +} diff --git a/libs/Nette/Forms/Controls/MultiSelectBox.php b/libs/Nette/Forms/Controls/MultiSelectBox.php index 13e14d5..9c39de9 100644 --- a/libs/Nette/Forms/Controls/MultiSelectBox.php +++ b/libs/Nette/Forms/Controls/MultiSelectBox.php @@ -1,111 +1,110 @@ -allowed); - if ($this->isFirstSkipped()) { - unset($allowed[0]); - } - return array_intersect($this->getRawValue(), $allowed); - } - - - - /** - * Returns selected keys (not checked). - * @return array - */ - public function getRawValue() - { - if (is_scalar($this->value)) { - $value = array($this->value); - - } elseif (!is_array($this->value)) { - $value = array(); - - } else { - $value = $this->value; - } - - $res = array(); - foreach ($value as $val) { - if (is_scalar($val)) { - $res[] = $val; - } - } - return $res; - } - - - - /** - * Returns selected values. - * @return array - */ - public function getSelectedItem() - { - if (!$this->areKeysUsed()) { - return $this->getValue(); - - } else { - $res = array(); - foreach ($this->getValue() as $value) { - $res[$value] = $this->allowed[$value]; - } - return $res; - } - } - - - - /** - * Returns HTML name of control. - * @return string - */ - public function getHtmlName() - { - return parent::getHtmlName() . '[]'; - } - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - $control = parent::getControl(); - $control->multiple = TRUE; - return $control; - } - -} +getRawValue(), array_keys($this->allowed)); + } + + + + /** + * Returns selected keys (not checked). + * @return array + */ + public function getRawValue() + { + if (is_scalar($this->value)) { + return array($this->value); + + } else { + $res = array(); + foreach ((array) $this->value as $val) { + if (is_scalar($val)) { + $res[] = $val; + } + } + return $res; + } + } + + + + /** + * Returns selected values. + * @return array + */ + public function getSelectedItem() + { + return $this->areKeysUsed() + ? array_intersect_key($this->allowed, array_flip($this->getValue())) + : $this->getValue(); + } + + + + /** + * Returns HTML name of control. + * @return string + */ + public function getHtmlName() + { + return parent::getHtmlName() . '[]'; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + return parent::getControl()->multiple(TRUE); + } + + + + /** + * Count/length validator. + * @param MultiSelectBox + * @param array min and max length pair + * @return bool + */ + public static function validateLength(MultiSelectBox $control, $range) + { + if (!is_array($range)) { + $range = array($range, $range); + } + $count = count($control->getSelectedItem()); + return ($range[0] === NULL || $count >= $range[0]) && ($range[1] === NULL || $count <= $range[1]); + } + +} diff --git a/libs/Nette/Forms/Controls/RadioList.php b/libs/Nette/Forms/Controls/RadioList.php index 642a4e6..4fb2fff 100644 --- a/libs/Nette/Forms/Controls/RadioList.php +++ b/libs/Nette/Forms/Controls/RadioList.php @@ -1,190 +1,190 @@ -control->type = 'radio'; - $this->container = Html::el(); - $this->separator = Html::el('br'); - if ($items !== NULL) { - $this->setItems($items); - } - } - - - - /** - * Returns selected radio value. - * @param bool - * @return mixed - */ - public function getValue($raw = FALSE) - { - return is_scalar($this->value) && ($raw || isset($this->items[$this->value])) ? $this->value : NULL; - } - - - - /** - * Has been any radio button selected? - * @return bool - */ - public function isFilled() - { - return $this->getValue() !== NULL; - } - - - - /** - * Sets options from which to choose. - * @param array - * @return RadioList provides a fluent interface - */ - public function setItems(array $items) - { - $this->items = $items; - return $this; - } - - - - /** - * Returns options from which to choose. - * @return array - */ - final public function getItems() - { - return $this->items; - } - - - - /** - * Returns separator HTML element template. - * @return Nette\Utils\Html - */ - final public function getSeparatorPrototype() - { - return $this->separator; - } - - - - /** - * Returns container HTML element template. - * @return Nette\Utils\Html - */ - final public function getContainerPrototype() - { - return $this->container; - } - - - - /** - * Generates control's HTML element. - * @param mixed - * @return Nette\Utils\Html - */ - public function getControl($key = NULL) - { - if ($key === NULL) { - $container = clone $this->container; - $separator = (string) $this->separator; - - } elseif (!isset($this->items[$key])) { - return NULL; - } - - $control = parent::getControl(); - $id = $control->id; - $counter = -1; - $value = $this->value === NULL ? NULL : (string) $this->getValue(); - $label = Html::el('label'); - - foreach ($this->items as $k => $val) { - $counter++; - if ($key !== NULL && $key != $k) { // intentionally == - continue; - } - - $control->id = $label->for = $id . '-' . $counter; - $control->checked = (string) $k === $value; - $control->value = $k; - - if ($val instanceof Html) { - $label->setHtml($val); - } else { - $label->setText($this->translate((string) $val)); - } - - if ($key !== NULL) { - return (string) $control . (string) $label; - } - - $container->add((string) $control . (string) $label . $separator); - $control->data('nette-rules', NULL); - // TODO: separator after last item? - } - - return $container; - } - - - - /** - * Generates label's HTML element. - * @param string - * @return void - */ - public function getLabel($caption = NULL) - { - $label = parent::getLabel($caption); - $label->for = NULL; - return $label; - } - -} +control->type = 'radio'; + $this->container = Html::el(); + $this->separator = Html::el('br'); + if ($items !== NULL) { + $this->setItems($items); + } + } + + + + /** + * Returns selected radio value. + * @param bool + * @return mixed + */ + public function getValue($raw = FALSE) + { + return is_scalar($this->value) && ($raw || isset($this->items[$this->value])) ? $this->value : NULL; + } + + + + /** + * Has been any radio button selected? + * @return bool + */ + public function isFilled() + { + return $this->getValue() !== NULL; + } + + + + /** + * Sets options from which to choose. + * @param array + * @return RadioList provides a fluent interface + */ + public function setItems(array $items) + { + $this->items = $items; + return $this; + } + + + + /** + * Returns options from which to choose. + * @return array + */ + final public function getItems() + { + return $this->items; + } + + + + /** + * Returns separator HTML element template. + * @return Nette\Utils\Html + */ + final public function getSeparatorPrototype() + { + return $this->separator; + } + + + + /** + * Returns container HTML element template. + * @return Nette\Utils\Html + */ + final public function getContainerPrototype() + { + return $this->container; + } + + + + /** + * Generates control's HTML element. + * @param mixed + * @return Nette\Utils\Html + */ + public function getControl($key = NULL) + { + if ($key === NULL) { + $container = clone $this->container; + $separator = (string) $this->separator; + + } elseif (!isset($this->items[$key])) { + return NULL; + } + + $control = parent::getControl(); + $id = $control->id; + $counter = -1; + $value = $this->value === NULL ? NULL : (string) $this->getValue(); + $label = Html::el('label'); + + foreach ($this->items as $k => $val) { + $counter++; + if ($key !== NULL && (string) $key !== (string) $k) { + continue; + } + + $control->id = $label->for = $id . '-' . $counter; + $control->checked = (string) $k === $value; + $control->value = $k; + + if ($val instanceof Html) { + $label->setHtml($val); + } else { + $label->setText($this->translate((string) $val)); + } + + if ($key !== NULL) { + return Html::el()->add($control)->add($label); + } + + $container->add((string) $control . (string) $label . $separator); + $control->data('nette-rules', NULL); + // TODO: separator after last item? + } + + return $container; + } + + + + /** + * Generates label's HTML element. + * @param string + * @return void + */ + public function getLabel($caption = NULL) + { + $label = parent::getLabel($caption); + $label->for = NULL; + return $label; + } + +} diff --git a/libs/Nette/Forms/Controls/SelectBox.php b/libs/Nette/Forms/Controls/SelectBox.php index ebf5f0d..25634ca 100644 --- a/libs/Nette/Forms/Controls/SelectBox.php +++ b/libs/Nette/Forms/Controls/SelectBox.php @@ -1,247 +1,242 @@ -control->setName('select'); - $this->control->size = $size > 1 ? (int) $size : NULL; - if ($items !== NULL) { - $this->setItems($items); - } - } - - - - /** - * Returns selected item key. - * @return mixed - */ - public function getValue() - { - $allowed = $this->allowed; - if ($this->skipFirst) { - $allowed = array_slice($allowed, 1, count($allowed), TRUE); - } - - return is_scalar($this->value) && isset($allowed[$this->value]) ? $this->value : NULL; - } - - - - /** - * Returns selected item key (not checked). - * @return mixed - */ - public function getRawValue() - { - return is_scalar($this->value) ? $this->value : NULL; - } - - - - /** - * Has been any item selected? - * @return bool - */ - public function isFilled() - { - $value = $this->getValue(); - return is_array($value) ? count($value) > 0 : $value !== NULL; - } - - - - /** - * Ignores the first item in select box. - * @param string - * @return SelectBox provides a fluent interface - */ - public function skipFirst($item = NULL) - { - if (is_bool($item)) { - $this->skipFirst = $item; - } else { - $this->skipFirst = TRUE; - if ($item !== NULL) { - $this->items = array('' => $item) + $this->items; - $this->allowed = array('' => '') + $this->allowed; - } - } - return $this; - } - - - - /** - * Is first item in select box ignored? - * @return bool - */ - final public function isFirstSkipped() - { - return $this->skipFirst; - } - - - - /** - * Are the keys used? - * @return bool - */ - final public function areKeysUsed() - { - return $this->useKeys; - } - - - - /** - * Sets items from which to choose. - * @param array - * @return SelectBox provides a fluent interface - */ - public function setItems(array $items, $useKeys = TRUE) - { - $this->items = $items; - $this->allowed = array(); - $this->useKeys = (bool) $useKeys; - - foreach ($items as $key => $value) { - if (!is_array($value)) { - $value = array($key => $value); - } - - foreach ($value as $key2 => $value2) { - if (!$this->useKeys) { - if (!is_scalar($value2)) { - throw new Nette\InvalidArgumentException("All items must be scalar."); - } - $key2 = $value2; - } - - if (isset($this->allowed[$key2])) { - throw new Nette\InvalidArgumentException("Items contain duplication for key '$key2'."); - } - - $this->allowed[$key2] = $value2; - } - } - return $this; - } - - - - /** - * Returns items from which to choose. - * @return array - */ - final public function getItems() - { - return $this->items; - } - - - - /** - * Returns selected value. - * @return string - */ - public function getSelectedItem() - { - if (!$this->useKeys) { - return $this->getValue(); - - } else { - $value = $this->getValue(); - return $value === NULL ? NULL : $this->allowed[$value]; - } - } - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - $control = parent::getControl(); - if ($this->skipFirst) { - reset($this->items); - $control->data('nette-empty-value', $this->useKeys ? key($this->items) : current($this->items)); - } - $selected = $this->getValue(); - $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE); - $option = Nette\Utils\Html::el('option'); - - foreach ($this->items as $key => $value) { - if (!is_array($value)) { - $value = array($key => $value); - $dest = $control; - - } else { - $dest = $control->create('optgroup')->label($key); - } - - foreach ($value as $key2 => $value2) { - if ($value2 instanceof Nette\Utils\Html) { - $dest->add((string) $value2->selected(isset($selected[$key2]))); - - } else { - $key2 = $this->useKeys ? $key2 : $value2; - $value2 = $this->translate((string) $value2); - $dest->add((string) $option->value($key2 === $value2 ? NULL : $key2) - ->selected(isset($selected[$key2])) - ->setText($value2)); - } - } - } - return $control; - } - -} +control->setName('select'); + $this->control->size = $size > 1 ? (int) $size : NULL; + if ($items !== NULL) { + $this->setItems($items); + } + } + + + + /** + * Returns selected item key. + * @return mixed + */ + public function getValue() + { + return is_scalar($this->value) && isset($this->allowed[$this->value]) ? $this->value : NULL; + } + + + + /** + * Returns selected item key (not checked). + * @return mixed + */ + public function getRawValue() + { + return is_scalar($this->value) ? $this->value : NULL; + } + + + + /** + * Has been any item selected? + * @return bool + */ + public function isFilled() + { + $value = $this->getValue(); + return is_array($value) ? count($value) > 0 : $value !== NULL; + } + + + + /** + * Sets first prompt item in select box. + * @param string + * @return SelectBox provides a fluent interface + */ + public function setPrompt($prompt) + { + if ($prompt === TRUE) { // back compatibility + $prompt = reset($this->items); + unset($this->allowed[key($this->items)], $this->items[key($this->items)]); + } + $this->prompt = $prompt; + return $this; + } + + + + /** @deprecated */ + function skipFirst($v = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use setPrompt() instead.', E_USER_WARNING); + return $this->setPrompt($v); + } + + + + /** + * Returns first prompt item? + * @return mixed + */ + final public function getPrompt() + { + return $this->prompt; + } + + + + /** + * Are the keys used? + * @return bool + */ + final public function areKeysUsed() + { + return $this->useKeys; + } + + + + /** + * Sets items from which to choose. + * @param array + * @param bool + * @return SelectBox provides a fluent interface + */ + public function setItems(array $items, $useKeys = TRUE) + { + $allowed = array(); + foreach ($items as $k => $v) { + foreach ((is_array($v) ? $v : array($k => $v)) as $key => $value) { + if (!$useKeys) { + if (!is_scalar($value)) { + throw new Nette\InvalidArgumentException("All items must be scalar."); + } + $key = $value; + } + + if (isset($allowed[$key])) { + throw new Nette\InvalidArgumentException("Items contain duplication for key '$key'."); + } + + $allowed[$key] = $value; + } + } + + $this->items = $items; + $this->allowed = $allowed; + $this->useKeys = (bool) $useKeys; + return $this; + } + + + + /** + * Returns items from which to choose. + * @return array + */ + final public function getItems() + { + return $this->items; + } + + + + /** + * Returns selected value. + * @return string + */ + public function getSelectedItem() + { + $value = $this->getValue(); + return ($this->useKeys && $value !== NULL) ? $this->allowed[$value] : $value; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $selected = $this->getValue(); + $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE); + $control = parent::getControl(); + $option = Nette\Utils\Html::el('option'); + + if ($this->prompt !== FALSE) { + $control->add($this->prompt instanceof Nette\Utils\Html + ? $this->prompt->value('') + : (string) $option->value('')->setText($this->translate((string) $this->prompt)) + ); + } + + foreach ($this->items as $key => $value) { + if (!is_array($value)) { + $value = array($key => $value); + $dest = $control; + } else { + $dest = $control->create('optgroup')->label($this->translate($key)); + } + + foreach ($value as $key2 => $value2) { + if ($value2 instanceof Nette\Utils\Html) { + $dest->add((string) $value2->selected(isset($selected[$key2]))); + + } else { + $key2 = $this->useKeys ? $key2 : $value2; + $value2 = $this->translate((string) $value2); + $dest->add((string) $option->value($key2) + ->selected(isset($selected[$key2])) + ->setText($value2)); + } + } + } + return $control; + } + +} diff --git a/libs/Nette/Forms/Controls/SubmitButton.php b/libs/Nette/Forms/Controls/SubmitButton.php index 51428ba..1704f60 100644 --- a/libs/Nette/Forms/Controls/SubmitButton.php +++ b/libs/Nette/Forms/Controls/SubmitButton.php @@ -1,126 +1,122 @@ -control->type = 'submit'; - } - - - - /** - * Sets 'pressed' indicator. - * @param bool - * @return SubmitButton provides a fluent interface - */ - public function setValue($value) - { - $this->value = is_scalar($value) && (bool) $value; - $form = $this->getForm(); - if ($this->value || !is_object($form->isSubmitted())) { - $this->value = TRUE; - $form->setSubmittedBy($this); - } - return $this; - } - - - - /** - * Tells if the form was submitted by this button. - * @return bool - */ - public function isSubmittedBy() - { - return $this->getForm()->isSubmitted() === $this; - } - - - - /** - * Sets the validation scope. Clicking the button validates only the controls within the specified scope. - * @param mixed - * @return SubmitButton provides a fluent interface - */ - public function setValidationScope($scope) - { - // TODO: implement groups - $this->validationScope = (bool) $scope; - $this->control->formnovalidate = !$this->validationScope; - return $this; - } - - - - /** - * Gets the validation scope. - * @return mixed - */ - final public function getValidationScope() - { - return $this->validationScope; - } - - - - /** - * Fires click event. - * @return void - */ - public function click() - { - $this->onClick($this); - } - - - - /** - * Submitted validator: has been button pressed? - * @param Nette\Forms\ISubmitterControl - * @return bool - */ - public static function validateSubmitted(Nette\Forms\ISubmitterControl $control) - { - return $control->isSubmittedBy(); - } - -} +control->type = 'submit'; + } + + + + /** + * Sets 'pressed' indicator. + * @param bool + * @return SubmitButton provides a fluent interface + */ + public function setValue($value) + { + if ($this->value = $value !== NULL) { + $this->getForm()->setSubmittedBy($this); + } + return $this; + } + + + + /** + * Tells if the form was submitted by this button. + * @return bool + */ + public function isSubmittedBy() + { + return $this->getForm()->isSubmitted() === $this; + } + + + + /** + * Sets the validation scope. Clicking the button validates only the controls within the specified scope. + * @param mixed + * @return SubmitButton provides a fluent interface + */ + public function setValidationScope($scope) + { + // TODO: implement groups + $this->validationScope = (bool) $scope; + $this->control->formnovalidate = !$this->validationScope; + return $this; + } + + + + /** + * Gets the validation scope. + * @return mixed + */ + final public function getValidationScope() + { + return $this->validationScope; + } + + + + /** + * Fires click event. + * @return void + */ + public function click() + { + $this->onClick($this); + } + + + + /** + * Submitted validator: has been button pressed? + * @return bool + */ + public static function validateSubmitted(Nette\Forms\ISubmitterControl $control) + { + return $control->isSubmittedBy(); + } + +} diff --git a/libs/Nette/Forms/Controls/TextArea.php b/libs/Nette/Forms/Controls/TextArea.php index 415c61e..b0c68b1 100644 --- a/libs/Nette/Forms/Controls/TextArea.php +++ b/libs/Nette/Forms/Controls/TextArea.php @@ -1,54 +1,53 @@ -control->setName('textarea'); - $this->control->cols = $cols; - $this->control->rows = $rows; - $this->value = ''; - } - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - $control = parent::getControl(); - $control->setText($this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value); - return $control; - } - -} +control->setName('textarea'); + $this->control->cols = $cols; + $this->control->rows = $rows; + $this->value = ''; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $control = parent::getControl(); + $control->setText($this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value); + return $control; + } + +} diff --git a/libs/Nette/Forms/Controls/TextBase.php b/libs/Nette/Forms/Controls/TextBase.php index 494389d..0bfee49 100644 --- a/libs/Nette/Forms/Controls/TextBase.php +++ b/libs/Nette/Forms/Controls/TextBase.php @@ -1,274 +1,265 @@ -value = is_scalar($value) ? (string) $value : ''; - return $this; - } - - - - /** - * Returns control's value. - * @return string - */ - public function getValue() - { - $value = $this->value; - foreach ($this->filters as $filter) { - $value = (string) $filter($value); - } - return $value === $this->translate($this->emptyValue) ? '' : $value; - } - - - - /** - * Sets the special value which is treated as empty string. - * @param string - * @return TextBase provides a fluent interface - */ - public function setEmptyValue($value) - { - $this->emptyValue = (string) $value; - return $this; - } - - - - /** - * Returns the special value which is treated as empty string. - * @return string - */ - final public function getEmptyValue() - { - return $this->emptyValue; - } - - - - /** - * Appends input string filter callback. - * @param callback - * @return TextBase provides a fluent interface - */ - public function addFilter($filter) - { - $this->filters[] = callback($filter); - return $this; - } - - - - public function getControl() - { - $control = parent::getControl(); - foreach ($this->getRules() as $rule) { - if ($rule->type === Nette\Forms\Rule::VALIDATOR && !$rule->isNegative - && ($rule->operation === Form::LENGTH || $rule->operation === Form::MAX_LENGTH) - ) { - $control->maxlength = is_array($rule->arg) ? $rule->arg[1] : $rule->arg; - } - } - if ($this->emptyValue !== '') { - $control->data('nette-empty-value', $this->translate($this->emptyValue)); - } - return $control; - } - - - - public function addRule($operation, $message = NULL, $arg = NULL) - { - if ($operation === Form::FLOAT) { - $this->addFilter(callback(__CLASS__, 'filterFloat')); - } - return parent::addRule($operation, $message, $arg); - } - - - - /** - * Min-length validator: has control's value minimal length? - * @param TextBase - * @param int length - * @return bool - */ - public static function validateMinLength(TextBase $control, $length) - { - return Strings::length($control->getValue()) >= $length; - } - - - - /** - * Max-length validator: is control's value length in limit? - * @param TextBase - * @param int length - * @return bool - */ - public static function validateMaxLength(TextBase $control, $length) - { - return Strings::length($control->getValue()) <= $length; - } - - - - /** - * Length validator: is control's value length in range? - * @param TextBase - * @param array min and max length pair - * @return bool - */ - public static function validateLength(TextBase $control, $range) - { - if (!is_array($range)) { - $range = array($range, $range); - } - $len = Strings::length($control->getValue()); - return ($range[0] === NULL || $len >= $range[0]) && ($range[1] === NULL || $len <= $range[1]); - } - - - - /** - * Email validator: is control's value valid email address? - * @param TextBase - * @return bool - */ - public static function validateEmail(TextBase $control) - { - $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part - $localPart = "(?:\"(?:[ !\\x23-\\x5B\\x5D-\\x7E]*|\\\\[ -~])+\"|$atom+(?:\\.$atom+)*)"; // quoted or unquoted - $chars = "a-z0-9\x80-\xFF"; // superset of IDN - $domain = "[$chars](?:[-$chars]{0,61}[$chars])"; // RFC 1034 one domain component - return (bool) Strings::match($control->getValue(), "(^$localPart@(?:$domain?\\.)+[-$chars]{2,19}\\z)i"); - } - - - - /** - * URL validator: is control's value valid URL? - * @param TextBase - * @return bool - */ - public static function validateUrl(TextBase $control) - { - $chars = "a-z0-9\x80-\xFF"; - return (bool) Strings::match( - $control->getValue(), - "#^(?:https?://|)(?:[$chars](?:[-$chars]{0,61}[$chars])?\\.)+[-$chars]{2,19}(/\S*)?$#i" - ); - } - - - - /** @deprecated */ - public static function validateRegexp(TextBase $control, $regexp) - { - return (bool) Strings::match($control->getValue(), $regexp); - } - - - - /** - * Regular expression validator: matches control's value regular expression? - * @param TextBase - * @param string - * @return bool - */ - public static function validatePattern(TextBase $control, $pattern) - { - return (bool) Strings::match($control->getValue(), "\x01^($pattern)$\x01u"); - } - - - - /** - * Integer validator: is a control's value decimal number? - * @param TextBase - * @return bool - */ - public static function validateInteger(TextBase $control) - { - return (bool) Strings::match($control->getValue(), '/^-?[0-9]+$/'); - } - - - - /** - * Float validator: is a control's value float number? - * @param TextBase - * @return bool - */ - public static function validateFloat(TextBase $control) - { - return (bool) Strings::match($control->getValue(), '/^-?[0-9]*[.,]?[0-9]+$/'); - } - - - - /** - * Rangle validator: is a control's value number in specified range? - * @param TextBase - * @param array min and max value pair - * @return bool - */ - public static function validateRange(TextBase $control, $range) - { - return ($range[0] === NULL || $control->getValue() >= $range[0]) - && ($range[1] === NULL || $control->getValue() <= $range[1]); - } - - - - /** - * Float string cleanup. - * @param string - * @return string - */ - public static function filterFloat($s) - { - return str_replace(array(' ', ','), array('', '.'), $s); - } - -} +value = is_array($value) ? '' : (string) $value; + return $this; + } + + + + /** + * Returns control's value. + * @return string + */ + public function getValue() + { + $value = $this->value; + foreach ($this->filters as $filter) { + $value = (string) $filter/*5.2*->invoke*/($value); + } + return $value === $this->translate($this->emptyValue) ? '' : $value; + } + + + + /** + * Sets the special value which is treated as empty string. + * @param string + * @return TextBase provides a fluent interface + */ + public function setEmptyValue($value) + { + $this->emptyValue = (string) $value; + return $this; + } + + + + /** + * Returns the special value which is treated as empty string. + * @return string + */ + final public function getEmptyValue() + { + return $this->emptyValue; + } + + + + /** + * Appends input string filter callback. + * @param callable + * @return TextBase provides a fluent interface + */ + public function addFilter($filter) + { + $this->filters[] = new Nette\Callback($filter); + return $this; + } + + + + public function getControl() + { + $control = parent::getControl(); + foreach ($this->getRules() as $rule) { + if ($rule->type === Nette\Forms\Rule::VALIDATOR && !$rule->isNegative + && ($rule->operation === Form::LENGTH || $rule->operation === Form::MAX_LENGTH) + ) { + $control->maxlength = is_array($rule->arg) ? $rule->arg[1] : $rule->arg; + } + } + if ($this->emptyValue !== '') { + $control->data('nette-empty-value', $this->translate($this->emptyValue)); + } + return $control; + } + + + + public function addRule($operation, $message = NULL, $arg = NULL) + { + if ($operation === Form::FLOAT) { + $this->addFilter(array(__CLASS__, 'filterFloat')); + } + return parent::addRule($operation, $message, $arg); + } + + + + /** + * Min-length validator: has control's value minimal length? + * @param TextBase + * @param int length + * @return bool + */ + public static function validateMinLength(TextBase $control, $length) + { + return Strings::length($control->getValue()) >= $length; + } + + + + /** + * Max-length validator: is control's value length in limit? + * @param TextBase + * @param int length + * @return bool + */ + public static function validateMaxLength(TextBase $control, $length) + { + return Strings::length($control->getValue()) <= $length; + } + + + + /** + * Length validator: is control's value length in range? + * @param TextBase + * @param array min and max length pair + * @return bool + */ + public static function validateLength(TextBase $control, $range) + { + if (!is_array($range)) { + $range = array($range, $range); + } + return Validators::isInRange(Strings::length($control->getValue()), $range); + } + + + + /** + * Email validator: is control's value valid email address? + * @param TextBase + * @return bool + */ + public static function validateEmail(TextBase $control) + { + return Validators::isEmail($control->getValue()); + } + + + + /** + * URL validator: is control's value valid URL? + * @param TextBase + * @return bool + */ + public static function validateUrl(TextBase $control) + { + return Validators::isUrl($control->getValue()) || Validators::isUrl('http://' . $control->getValue()); + } + + + + /** @deprecated */ + public static function validateRegexp(TextBase $control, $regexp) + { + return (bool) Strings::match($control->getValue(), $regexp); + } + + + + /** + * Regular expression validator: matches control's value regular expression? + * @param TextBase + * @param string + * @return bool + */ + public static function validatePattern(TextBase $control, $pattern) + { + return (bool) Strings::match($control->getValue(), "\x01^($pattern)$\x01u"); + } + + + + /** + * Integer validator: is a control's value decimal number? + * @param TextBase + * @return bool + */ + public static function validateInteger(TextBase $control) + { + return Validators::isNumericInt($control->getValue()); + } + + + + /** + * Float validator: is a control's value float number? + * @param TextBase + * @return bool + */ + public static function validateFloat(TextBase $control) + { + return Validators::isNumeric(static::filterFloat($control->getValue())); + } + + + + /** + * Rangle validator: is a control's value number in specified range? + * @param TextBase + * @param array min and max value pair + * @return bool + */ + public static function validateRange(TextBase $control, $range) + { + return Validators::isInRange($control->getValue(), $range); + } + + + + /** + * Float string cleanup. + * @param string + * @return string + */ + public static function filterFloat($s) + { + return str_replace(array(' ', ','), array('', '.'), $s); + } + +} diff --git a/libs/Nette/Forms/Controls/TextInput.php b/libs/Nette/Forms/Controls/TextInput.php index 893bb59..46b672e 100644 --- a/libs/Nette/Forms/Controls/TextInput.php +++ b/libs/Nette/Forms/Controls/TextInput.php @@ -1,103 +1,104 @@ -control->type = 'text'; - $this->control->size = $cols; - $this->control->maxlength = $maxLength; - $this->filters[] = callback($this, 'sanitize'); - $this->value = ''; - } - - - - /** - * Filter: removes unnecessary whitespace and shortens value to control's max length. - * @return string - */ - public function sanitize($value) - { - if ($this->control->maxlength && Nette\Utils\Strings::length($value) > $this->control->maxlength) { - $value = iconv_substr($value, 0, $this->control->maxlength, 'UTF-8'); - } - return Nette\Utils\Strings::trim(strtr($value, "\r\n", ' ')); - } - - - - /** - * Changes control's type attribute. - * @param string - * @return BaseControl provides a fluent interface - */ - public function setType($type) - { - $this->control->type = $type; - return $this; - } - - - - /** @deprecated */ - public function setPasswordMode($mode = TRUE) - { - $this->control->type = $mode ? 'password' : 'text'; - return $this; - } - - - - /** - * Generates control's HTML element. - * @return Nette\Utils\Html - */ - public function getControl() - { - $control = parent::getControl(); - foreach ($this->getRules() as $rule) { - if ($rule->isNegative || $rule->type !== Nette\Forms\Rule::VALIDATOR) { - - } elseif ($rule->operation === Nette\Forms\Form::RANGE && $control->type !== 'text') { - list($control->min, $control->max) = $rule->arg; - - } elseif ($rule->operation === Nette\Forms\Form::PATTERN) { - $control->pattern = $rule->arg; - } - } - if ($control->type !== 'password') { - $control->value = $this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value; - } - return $control; - } - -} +control->type = 'text'; + $this->control->size = $cols; + $this->control->maxlength = $maxLength; + $this->addFilter($this->sanitize); + $this->value = ''; + } + + + + /** + * Filter: removes unnecessary whitespace and shortens value to control's max length. + * @return string + */ + public function sanitize($value) + { + if ($this->control->maxlength && Nette\Utils\Strings::length($value) > $this->control->maxlength) { + $value = Nette\Utils\Strings::substring($value, 0, $this->control->maxlength); + } + return Nette\Utils\Strings::trim(strtr($value, "\r\n", ' ')); + } + + + + /** + * Changes control's type attribute. + * @param string + * @return BaseControl provides a fluent interface + */ + public function setType($type) + { + $this->control->type = $type; + return $this; + } + + + + /** @deprecated */ + public function setPasswordMode($mode = TRUE) + { + $this->control->type = $mode ? 'password' : 'text'; + return $this; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $control = parent::getControl(); + foreach ($this->getRules() as $rule) { + if ($rule->isNegative || $rule->type !== Nette\Forms\Rule::VALIDATOR) { + + } elseif ($rule->operation === Nette\Forms\Form::RANGE && $control->type !== 'text') { + list($control->min, $control->max) = $rule->arg; + + } elseif ($rule->operation === Nette\Forms\Form::PATTERN) { + $control->pattern = $rule->arg; + } + } + if ($control->type !== 'password') { + $control->value = $this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value; + } + return $control; + } + +} diff --git a/libs/Nette/Forms/Controls/UploadControl.php b/libs/Nette/Forms/Controls/UploadControl.php index 0c0a0dd..82b546b 100644 --- a/libs/Nette/Forms/Controls/UploadControl.php +++ b/libs/Nette/Forms/Controls/UploadControl.php @@ -1,138 +1,137 @@ -control->type = 'file'; - } - - - - /** - * This method will be called when the component (or component's parent) - * becomes attached to a monitored object. Do not call this method yourself. - * @param Nette\Forms\IComponent - * @return void - */ - protected function attached($form) - { - if ($form instanceof Nette\Forms\Form) { - if ($form->getMethod() !== Nette\Forms\Form::POST) { - throw new Nette\InvalidStateException('File upload requires method POST.'); - } - $form->getElementPrototype()->enctype = 'multipart/form-data'; - } - parent::attached($form); - } - - - - /** - * Sets control's value. - * @param array|Nette\Http\FileUpload - * @return Nette\Http\FileUpload provides a fluent interface - */ - public function setValue($value) - { - if (is_array($value)) { - $this->value = new Http\FileUpload($value); - - } elseif ($value instanceof Http\FileUpload) { - $this->value = $value; - - } else { - $this->value = new Http\FileUpload(NULL); - } - return $this; - } - - - - /** - * Has been any file uploaded? - * @return bool - */ - public function isFilled() - { - return $this->value instanceof Http\FileUpload && $this->value->isOK(); - } - - - - /** - * FileSize validator: is file size in limit? - * @param UploadControl - * @param int file size limit - * @return bool - */ - public static function validateFileSize(UploadControl $control, $limit) - { - $file = $control->getValue(); - return $file instanceof Http\FileUpload && $file->getSize() <= $limit; - } - - - - /** - * MimeType validator: has file specified mime type? - * @param UploadControl - * @param array|string mime type - * @return bool - */ - public static function validateMimeType(UploadControl $control, $mimeType) - { - $file = $control->getValue(); - if ($file instanceof Http\FileUpload) { - $type = strtolower($file->getContentType()); - $mimeTypes = is_array($mimeType) ? $mimeType : explode(',', $mimeType); - if (in_array($type, $mimeTypes, TRUE)) { - return TRUE; - } - if (in_array(preg_replace('#/.*#', '/*', $type), $mimeTypes, TRUE)) { - return TRUE; - } - } - return FALSE; - } - - - - /** - * Image validator: is file image? - * @param UploadControl - * @return bool - */ - public static function validateImage(UploadControl $control) - { - $file = $control->getValue(); - return $file instanceof Http\FileUpload && $file->isImage(); - } - -} +control->type = 'file'; + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param Nette\Forms\IComponent + * @return void + */ + protected function attached($form) + { + if ($form instanceof Nette\Forms\Form) { + if ($form->getMethod() !== Nette\Forms\Form::POST) { + throw new Nette\InvalidStateException('File upload requires method POST.'); + } + $form->getElementPrototype()->enctype = 'multipart/form-data'; + } + parent::attached($form); + } + + + + /** + * Sets control's value. + * @param array|Nette\Http\FileUpload + * @return Nette\Http\FileUpload provides a fluent interface + */ + public function setValue($value) + { + if (is_array($value)) { + $this->value = new Http\FileUpload($value); + + } elseif ($value instanceof Http\FileUpload) { + $this->value = $value; + + } else { + $this->value = new Http\FileUpload(NULL); + } + return $this; + } + + + + /** + * Has been any file uploaded? + * @return bool + */ + public function isFilled() + { + return $this->value instanceof Http\FileUpload && $this->value->isOK(); + } + + + + /** + * FileSize validator: is file size in limit? + * @param UploadControl + * @param int file size limit + * @return bool + */ + public static function validateFileSize(UploadControl $control, $limit) + { + $file = $control->getValue(); + return $file instanceof Http\FileUpload && $file->getSize() <= $limit; + } + + + + /** + * MimeType validator: has file specified mime type? + * @param UploadControl + * @param array|string mime type + * @return bool + */ + public static function validateMimeType(UploadControl $control, $mimeType) + { + $file = $control->getValue(); + if ($file instanceof Http\FileUpload) { + $type = strtolower($file->getContentType()); + $mimeTypes = is_array($mimeType) ? $mimeType : explode(',', $mimeType); + if (in_array($type, $mimeTypes, TRUE)) { + return TRUE; + } + if (in_array(preg_replace('#/.*#', '/*', $type), $mimeTypes, TRUE)) { + return TRUE; + } + } + return FALSE; + } + + + + /** + * Image validator: is file image? + * @return bool + */ + public static function validateImage(UploadControl $control) + { + $file = $control->getValue(); + return $file instanceof Http\FileUpload && $file->isImage(); + } + +} diff --git a/libs/Nette/Forms/Form.php b/libs/Nette/Forms/Form.php index 8303924..f18dd5f 100644 --- a/libs/Nette/Forms/Form.php +++ b/libs/Nette/Forms/Form.php @@ -1,618 +1,641 @@ - element */ - private $element; - - /** @var IFormRenderer */ - private $renderer; - - /** @var Nette\Localization\ITranslator */ - private $translator; - - /** @var array of ControlGroup */ - private $groups = array(); - - /** @var array */ - private $errors = array(); - - - - /** - * Form constructor. - * @param string - */ - public function __construct($name = NULL) - { - $this->element = Nette\Utils\Html::el('form'); - $this->element->action = ''; // RFC 1808 -> empty uri means 'this' - $this->element->method = self::POST; - $this->element->id = 'frm-' . $name; - - $this->monitor(__CLASS__); - if ($name !== NULL) { - $tracker = new Controls\HiddenField($name); - $tracker->unmonitor(__CLASS__); - $this[self::TRACKER_ID] = $tracker; - } - parent::__construct(NULL, $name); - } - - - - /** - * This method will be called when the component (or component's parent) - * becomes attached to a monitored object. Do not call this method yourself. - * @param IComponent - * @return void - */ - protected function attached($obj) - { - if ($obj instanceof self) { - throw new Nette\InvalidStateException('Nested forms are forbidden.'); - } - } - - - - /** - * Returns self. - * @return Form - */ - final public function getForm($need = TRUE) - { - return $this; - } - - - - /** - * Sets form's action. - * @param mixed URI - * @return Form provides a fluent interface - */ - public function setAction($url) - { - $this->element->action = $url; - return $this; - } - - - - /** - * Returns form's action. - * @return mixed URI - */ - public function getAction() - { - return $this->element->action; - } - - - - /** - * Sets form's method. - * @param string get | post - * @return Form provides a fluent interface - */ - public function setMethod($method) - { - if ($this->httpData !== NULL) { - throw new Nette\InvalidStateException(__METHOD__ . '() must be called until the form is empty.'); - } - $this->element->method = strtolower($method); - return $this; - } - - - - /** - * Returns form's method. - * @return string get | post - */ - public function getMethod() - { - return $this->element->method; - } - - - - /** - * Cross-Site Request Forgery (CSRF) form protection. - * @param string - * @param int - * @return void - */ - public function addProtection($message = NULL, $timeout = NULL) - { - $session = $this->getSession()->getNamespace('Nette.Forms.Form/CSRF'); - $key = "key$timeout"; - if (isset($session->$key)) { - $token = $session->$key; - } else { - $session->$key = $token = Nette\Utils\Strings::random(); - } - $session->setExpiration($timeout, $key); - $this[self::PROTECTOR_ID] = new Controls\HiddenField($token); - $this[self::PROTECTOR_ID]->addRule(self::PROTECTION, $message, $token); - } - - - - /** - * Adds fieldset group to the form. - * @param string caption - * @param bool set this group as current - * @return ControlGroup - */ - public function addGroup($caption = NULL, $setAsCurrent = TRUE) - { - $group = new ControlGroup; - $group->setOption('label', $caption); - $group->setOption('visual', TRUE); - - if ($setAsCurrent) { - $this->setCurrentGroup($group); - } - - if (isset($this->groups[$caption])) { - return $this->groups[] = $group; - } else { - return $this->groups[$caption] = $group; - } - } - - - - /** - * Removes fieldset group from form. - * @param string|FormGroup - * @return void - */ - public function removeGroup($name) - { - if (is_string($name) && isset($this->groups[$name])) { - $group = $this->groups[$name]; - - } elseif ($name instanceof ControlGroup && in_array($name, $this->groups, TRUE)) { - $group = $name; - $name = array_search($group, $this->groups, TRUE); - - } else { - throw new Nette\InvalidArgumentException("Group not found in form '$this->name'"); - } - - foreach ($group->getControls() as $control) { - $this->removeComponent($control); - } - - unset($this->groups[$name]); - } - - - - /** - * Returns all defined groups. - * @return array of FormGroup - */ - public function getGroups() - { - return $this->groups; - } - - - - /** - * Returns the specified group. - * @param string name - * @return ControlGroup - */ - public function getGroup($name) - { - return isset($this->groups[$name]) ? $this->groups[$name] : NULL; - } - - - - /********************* translator ****************d*g**/ - - - - /** - * Sets translate adapter. - * @param Nette\Localization\ITranslator - * @return Form provides a fluent interface - */ - public function setTranslator(Nette\Localization\ITranslator $translator = NULL) - { - $this->translator = $translator; - return $this; - } - - - - /** - * Returns translate adapter. - * @return Nette\Localization\ITranslator|NULL - */ - final public function getTranslator() - { - return $this->translator; - } - - - - /********************* submission ****************d*g**/ - - - - /** - * Tells if the form is anchored. - * @return bool - */ - public function isAnchored() - { - return TRUE; - } - - - - /** - * Tells if the form was submitted. - * @return ISubmitterControl|FALSE submittor control - */ - final public function isSubmitted() - { - if ($this->submittedBy === NULL) { - $this->getHttpData(); - $this->submittedBy = !empty($this->httpData); - } - return $this->submittedBy; - } - - - - /** - * Sets the submittor control. - * @param ISubmitterControl - * @return Form provides a fluent interface - */ - public function setSubmittedBy(ISubmitterControl $by = NULL) - { - $this->submittedBy = $by === NULL ? FALSE : $by; - return $this; - } - - - - /** - * Returns submitted HTTP data. - * @return array - */ - final public function getHttpData() - { - if ($this->httpData === NULL) { - if (!$this->isAnchored()) { - throw new Nette\InvalidStateException('Form is not anchored and therefore can not determine whether it was submitted.'); - } - $this->httpData = (array) $this->receiveHttpData(); - } - return $this->httpData; - } - - - - /** - * Fires submit/click events. - * @return void - */ - public function fireEvents() - { - if (!$this->isSubmitted()) { - return; - - } elseif ($this->submittedBy instanceof ISubmitterControl) { - if (!$this->submittedBy->getValidationScope() || $this->isValid()) { - $this->submittedBy->click(); - $this->onSubmit($this); - } else { - $this->submittedBy->onInvalidClick($this->submittedBy); - $this->onInvalidSubmit($this); - } - - } elseif ($this->isValid()) { - $this->onSubmit($this); - - } else { - $this->onInvalidSubmit($this); - } - } - - - - /** - * Internal: receives submitted HTTP data. - * @return array - */ - protected function receiveHttpData() - { - $httpRequest = $this->getHttpRequest(); - if (strcasecmp($this->getMethod(), $httpRequest->getMethod())) { - return; - } - - if ($httpRequest->isMethod('post')) { - $data = Nette\Utils\Arrays::mergeTree($httpRequest->getPost(), $httpRequest->getFiles()); - } else { - $data = $httpRequest->getQuery(); - } - - if ($tracker = $this->getComponent(self::TRACKER_ID, FALSE)) { - if (!isset($data[self::TRACKER_ID]) || $data[self::TRACKER_ID] !== $tracker->getValue()) { - return; - } - } - - return $data; - } - - - - /********************* data exchange ****************d*g**/ - - - - /** - * Returns the values submitted by the form. - * @return array - */ - public function getValues() - { - $values = parent::getValues(); - unset($values[self::TRACKER_ID], $values[self::PROTECTOR_ID]); - return $values; - } - - - - /********************* validation ****************d*g**/ - - - - /** - * Adds error message to the list. - * @param string error message - * @return void - */ - public function addError($message) - { - $this->valid = FALSE; - if ($message !== NULL && !in_array($message, $this->errors, TRUE)) { - $this->errors[] = $message; - } - } - - - - /** - * Returns validation errors. - * @return array - */ - public function getErrors() - { - return $this->errors; - } - - - - /** - * @return bool - */ - public function hasErrors() - { - return (bool) $this->getErrors(); - } - - - - /** - * @return void - */ - public function cleanErrors() - { - $this->errors = array(); - $this->valid = NULL; - } - - - - /********************* rendering ****************d*g**/ - - - - /** - * Returns form's HTML element template. - * @return Nette\Utils\Html - */ - public function getElementPrototype() - { - return $this->element; - } - - - - /** - * Sets form renderer. - * @param IFormRenderer - * @return Form provides a fluent interface - */ - public function setRenderer(IFormRenderer $renderer) - { - $this->renderer = $renderer; - return $this; - } - - - - /** - * Returns form renderer. - * @return IFormRenderer - */ - final public function getRenderer() - { - if ($this->renderer === NULL) { - $this->renderer = new Rendering\DefaultFormRenderer; - } - return $this->renderer; - } - - - - /** - * Renders form. - * @return void - */ - public function render() - { - $args = func_get_args(); - array_unshift($args, $this); - echo call_user_func_array(array($this->getRenderer(), 'render'), $args); - } - - - - /** - * Renders form to string. - * @return bool can throw exceptions? (hidden parameter) - * @return string - */ - public function __toString() - { - try { - return $this->getRenderer()->render($this); - - } catch (\Exception $e) { - if (func_get_args() && func_get_arg(0)) { - throw $e; - } else { - Nette\Diagnostics\Debugger::toStringException($e); - } - } - } - - - - /********************* backend ****************d*g**/ - - - - /** - * @return Nette\Http\IRequest - */ - protected function getHttpRequest() - { - return Nette\Environment::getHttpRequest(); - } - - - - /** - * @return Nette\Http\Session - */ - protected function getSession() - { - return Nette\Environment::getSession(); - } - -} + element */ + private $element; + + /** @var IFormRenderer */ + private $renderer; + + /** @var Nette\Localization\ITranslator */ + private $translator; + + /** @var ControlGroup[] */ + private $groups = array(); + + /** @var array */ + private $errors = array(); + + + + /** + * Form constructor. + * @param string + */ + public function __construct($name = NULL) + { + $this->element = Nette\Utils\Html::el('form'); + $this->element->action = ''; // RFC 1808 -> empty uri means 'this' + $this->element->method = self::POST; + $this->element->id = $name === NULL ? NULL : 'frm-' . $name; + + $this->monitor(__CLASS__); + if ($name !== NULL) { + $tracker = new Controls\HiddenField($name); + $tracker->unmonitor(__CLASS__); + $this[self::TRACKER_ID] = $tracker; + } + parent::__construct(NULL, $name); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param IComponent + * @return void + */ + protected function attached($obj) + { + if ($obj instanceof self) { + throw new Nette\InvalidStateException('Nested forms are forbidden.'); + } + } + + + + /** + * Returns self. + * @return Form + */ + final public function getForm($need = TRUE) + { + return $this; + } + + + + /** + * Sets form's action. + * @param mixed URI + * @return Form provides a fluent interface + */ + public function setAction($url) + { + $this->element->action = $url; + return $this; + } + + + + /** + * Returns form's action. + * @return mixed URI + */ + public function getAction() + { + return $this->element->action; + } + + + + /** + * Sets form's method. + * @param string get | post + * @return Form provides a fluent interface + */ + public function setMethod($method) + { + if ($this->httpData !== NULL) { + throw new Nette\InvalidStateException(__METHOD__ . '() must be called until the form is empty.'); + } + $this->element->method = strtolower($method); + return $this; + } + + + + /** + * Returns form's method. + * @return string get | post + */ + public function getMethod() + { + return $this->element->method; + } + + + + /** + * Cross-Site Request Forgery (CSRF) form protection. + * @param string + * @param int + * @return void + */ + public function addProtection($message = NULL, $timeout = NULL) + { + $session = $this->getSession()->getSection('Nette.Forms.Form/CSRF'); + $key = "key$timeout"; + if (isset($session->$key)) { + $token = $session->$key; + } else { + $session->$key = $token = Nette\Utils\Strings::random(); + } + $session->setExpiration($timeout, $key); + $this[self::PROTECTOR_ID] = new Controls\HiddenField($token); + $this[self::PROTECTOR_ID]->addRule(self::PROTECTION, $message, $token); + } + + + + /** + * Adds fieldset group to the form. + * @param string caption + * @param bool set this group as current + * @return ControlGroup + */ + public function addGroup($caption = NULL, $setAsCurrent = TRUE) + { + $group = new ControlGroup; + $group->setOption('label', $caption); + $group->setOption('visual', TRUE); + + if ($setAsCurrent) { + $this->setCurrentGroup($group); + } + + if (isset($this->groups[$caption])) { + return $this->groups[] = $group; + } else { + return $this->groups[$caption] = $group; + } + } + + + + /** + * Removes fieldset group from form. + * @param string|FormGroup + * @return void + */ + public function removeGroup($name) + { + if (is_string($name) && isset($this->groups[$name])) { + $group = $this->groups[$name]; + + } elseif ($name instanceof ControlGroup && in_array($name, $this->groups, TRUE)) { + $group = $name; + $name = array_search($group, $this->groups, TRUE); + + } else { + throw new Nette\InvalidArgumentException("Group not found in form '$this->name'"); + } + + foreach ($group->getControls() as $control) { + $this->removeComponent($control); + } + + unset($this->groups[$name]); + } + + + + /** + * Returns all defined groups. + * @return FormGroup[] + */ + public function getGroups() + { + return $this->groups; + } + + + + /** + * Returns the specified group. + * @param string name + * @return ControlGroup + */ + public function getGroup($name) + { + return isset($this->groups[$name]) ? $this->groups[$name] : NULL; + } + + + + /********************* translator ****************d*g**/ + + + + /** + * Sets translate adapter. + * @return Form provides a fluent interface + */ + public function setTranslator(Nette\Localization\ITranslator $translator = NULL) + { + $this->translator = $translator; + return $this; + } + + + + /** + * Returns translate adapter. + * @return Nette\Localization\ITranslator|NULL + */ + final public function getTranslator() + { + return $this->translator; + } + + + + /********************* submission ****************d*g**/ + + + + /** + * Tells if the form is anchored. + * @return bool + */ + public function isAnchored() + { + return TRUE; + } + + + + /** + * Tells if the form was submitted. + * @return ISubmitterControl|FALSE submittor control + */ + final public function isSubmitted() + { + if ($this->submittedBy === NULL && count($this->getControls())) { + $this->getHttpData(); + $this->submittedBy = $this->httpData !== NULL; + } + return $this->submittedBy; + } + + + + /** + * Tells if the form was submitted and successfully validated. + * @return bool + */ + final public function isSuccess() + { + return $this->isSubmitted() && $this->isValid(); + } + + + + /** + * Sets the submittor control. + * @return Form provides a fluent interface + */ + public function setSubmittedBy(ISubmitterControl $by = NULL) + { + $this->submittedBy = $by === NULL ? FALSE : $by; + return $this; + } + + + + /** + * Returns submitted HTTP data. + * @return array + */ + final public function getHttpData() + { + if ($this->httpData === NULL) { + if (!$this->isAnchored()) { + throw new Nette\InvalidStateException('Form is not anchored and therefore can not determine whether it was submitted.'); + } + $this->httpData = $this->receiveHttpData(); + } + return $this->httpData; + } + + + + /** + * Fires submit/click events. + * @return void + */ + public function fireEvents() + { + if (!$this->isSubmitted()) { + return; + + } elseif ($this->submittedBy instanceof ISubmitterControl) { + if (!$this->submittedBy->getValidationScope() || $this->isValid()) { + $this->submittedBy->click(); + $valid = TRUE; + } else { + $this->submittedBy->onInvalidClick($this->submittedBy); + } + } + + if (isset($valid) || $this->isValid()) { + $this->onSuccess($this); + } else { + $this->onError($this); + if ($this->onInvalidSubmit) { + trigger_error(__CLASS__ . '->onInvalidSubmit is deprecated; use onError instead.', E_USER_WARNING); + $this->onInvalidSubmit($this); + } + } + + if ($this->onSuccess) { // back compatibility + $this->onSubmit($this); + } elseif ($this->onSubmit) { + trigger_error(__CLASS__ . '->onSubmit changed its behavior; use onSuccess instead.', E_USER_WARNING); + if (isset($valid) || $this->isValid()) { + $this->onSubmit($this); + } + } + } + + + + /** + * Internal: receives submitted HTTP data. + * @return array + */ + protected function receiveHttpData() + { + $httpRequest = $this->getHttpRequest(); + if (strcasecmp($this->getMethod(), $httpRequest->getMethod())) { + return; + } + + if ($httpRequest->isMethod('post')) { + $data = Nette\Utils\Arrays::mergeTree($httpRequest->getPost(), $httpRequest->getFiles()); + } else { + $data = $httpRequest->getQuery(); + } + + if ($tracker = $this->getComponent(self::TRACKER_ID, FALSE)) { + if (!isset($data[self::TRACKER_ID]) || $data[self::TRACKER_ID] !== $tracker->getValue()) { + return; + } + } + + return $data; + } + + + + /********************* data exchange ****************d*g**/ + + + + /** + * Returns the values submitted by the form. + * @return Nette\ArrayHash|array + */ + public function getValues($asArray = FALSE) + { + $values = parent::getValues($asArray); + unset($values[self::TRACKER_ID], $values[self::PROTECTOR_ID]); + return $values; + } + + + + /********************* validation ****************d*g**/ + + + + /** + * Adds error message to the list. + * @param string error message + * @return void + */ + public function addError($message) + { + $this->valid = FALSE; + if ($message !== NULL && !in_array($message, $this->errors, TRUE)) { + $this->errors[] = $message; + } + } + + + + /** + * Returns validation errors. + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + + + /** + * @return bool + */ + public function hasErrors() + { + return (bool) $this->getErrors(); + } + + + + /** + * @return void + */ + public function cleanErrors() + { + $this->errors = array(); + $this->valid = NULL; + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Returns form's HTML element template. + * @return Nette\Utils\Html + */ + public function getElementPrototype() + { + return $this->element; + } + + + + /** + * Sets form renderer. + * @return Form provides a fluent interface + */ + public function setRenderer(IFormRenderer $renderer) + { + $this->renderer = $renderer; + return $this; + } + + + + /** + * Returns form renderer. + * @return IFormRenderer + */ + final public function getRenderer() + { + if ($this->renderer === NULL) { + $this->renderer = new Rendering\DefaultFormRenderer; + } + return $this->renderer; + } + + + + /** + * Renders form. + * @return void + */ + public function render() + { + $args = func_get_args(); + array_unshift($args, $this); + echo call_user_func_array(array($this->getRenderer(), 'render'), $args); + } + + + + /** + * Renders form to string. + * @return bool can throw exceptions? (hidden parameter) + * @return string + */ + public function __toString() + { + try { + return $this->getRenderer()->render($this); + + } catch (\Exception $e) { + if (func_get_args() && func_get_arg(0)) { + throw $e; + } else { + trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR); + } + } + } + + + + /********************* backend ****************d*g**/ + + + + /** + * @return Nette\Http\IRequest + */ + protected function getHttpRequest() + { + return Nette\Environment::getHttpRequest(); + } + + + + /** + * @return Nette\Http\Session + */ + protected function getSession() + { + return Nette\Environment::getSession(); + } + +} diff --git a/libs/Nette/Forms/IControl.php b/libs/Nette/Forms/IControl.php index bdae452..d6845f9 100644 --- a/libs/Nette/Forms/IControl.php +++ b/libs/Nette/Forms/IControl.php @@ -1,70 +1,70 @@ - array( - 'container' => NULL, - 'errors' => TRUE, - ), - - 'error' => array( - 'container' => 'ul class=error', - 'item' => 'li', - ), - - 'group' => array( - 'container' => 'fieldset', - 'label' => 'legend', - 'description' => 'p', - ), - - 'controls' => array( - 'container' => 'table', - ), - - 'pair' => array( - 'container' => 'tr', - '.required' => 'required', - '.optional' => NULL, - '.odd' => NULL, - ), - - 'control' => array( - 'container' => 'td', - '.odd' => NULL, - - 'errors' => FALSE, - 'description' => 'small', - 'requiredsuffix' => '', - - '.required' => 'required', - '.text' => 'text', - '.password' => 'text', - '.file' => 'text', - '.submit' => 'button', - '.image' => 'imagebutton', - '.button' => 'button', - ), - - 'label' => array( - 'container' => 'th', - 'suffix' => NULL, - 'requiredsuffix' => '', - ), - - 'hidden' => array( - 'container' => 'div', - ), - ); - - /** @var Nette\Forms\Form */ - protected $form; - - /** @var int */ - protected $counter; - - - - /** - * Provides complete form rendering. - * @param Nette\Forms\Form - * @param string 'begin', 'errors', 'body', 'end' or empty to render all - * @return string - */ - public function render(Nette\Forms\Form $form, $mode = NULL) - { - if ($this->form !== $form) { - $this->form = $form; - $this->init(); - } - - $s = ''; - if (!$mode || $mode === 'begin') { - $s .= $this->renderBegin(); - } - if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') { - $s .= $this->renderErrors(); - } - if (!$mode || $mode === 'body') { - $s .= $this->renderBody(); - } - if (!$mode || $mode === 'end') { - $s .= $this->renderEnd(); - } - return $s; - } - - - - /** @deprecated */ - public function setClientScript() - { - trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING); - return $this; - } - - - - /** - * Initializes form. - * @return void - */ - protected function init() - { - // TODO: only for back compatiblity - remove? - $wrapper = & $this->wrappers['control']; - foreach ($this->form->getControls() as $control) { - if ($control->isRequired() && isset($wrapper['.required'])) { - $control->getLabelPrototype()->class($wrapper['.required'], TRUE); - } - - $el = $control->getControlPrototype(); - if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) { - $el->class($wrapper['.' . $el->type], TRUE); - } - } - } - - - - /** - * Renders form begin. - * @return string - */ - public function renderBegin() - { - $this->counter = 0; - - foreach ($this->form->getControls() as $control) { - $control->setOption('rendered', FALSE); - } - - if (strcasecmp($this->form->getMethod(), 'get') === 0) { - $el = clone $this->form->getElementPrototype(); - $url = explode('?', (string) $el->action, 2); - $el->action = $url[0]; - $s = ''; - if (isset($url[1])) { - foreach (preg_split('#[;&]#', $url[1]) as $param) { - $parts = explode('=', $param, 2); - $name = urldecode($parts[0]); - if (!isset($this->form[$name])) { - $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1]))); - } - } - $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s); - } - return $el->startTag() . $s; - - - } else { - return $this->form->getElementPrototype()->startTag(); - } - } - - - - /** - * Renders form end. - * @return string - */ - public function renderEnd() - { - $s = ''; - foreach ($this->form->getControls() as $control) { - if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) { - $s .= (string) $control->getControl(); - } - } - if ($s) { - $s = $this->getWrapper('hidden container')->setHtml($s) . "\n"; - } - - return $s . $this->form->getElementPrototype()->endTag() . "\n"; - } - - - - /** - * Renders validation errors (per form or per control). - * @param Nette\Forms\IControl - * @return string - */ - public function renderErrors(Nette\Forms\IControl $control = NULL) - { - $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors(); - if (count($errors)) { - $ul = $this->getWrapper('error container'); - $li = $this->getWrapper('error item'); - - foreach ($errors as $error) { - $item = clone $li; - if ($error instanceof Html) { - $item->add($error); - } else { - $item->setText($error); - } - $ul->add($item); - } - return "\n" . $ul->render(0); - } - } - - - - /** - * Renders form body. - * @return string - */ - public function renderBody() - { - $s = $remains = ''; - - $defaultContainer = $this->getWrapper('group container'); - $translator = $this->form->getTranslator(); - - foreach ($this->form->getGroups() as $group) { - if (!$group->getControls() || !$group->getOption('visual')) { - continue; - } - - $container = $group->getOption('container', $defaultContainer); - $container = $container instanceof Html ? clone $container : Html::el($container); - - $s .= "\n" . $container->startTag(); - - $text = $group->getOption('label'); - if ($text instanceof Html) { - $s .= $text; - - } elseif (is_string($text)) { - if ($translator !== NULL) { - $text = $translator->translate($text); - } - $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n"; - } - - $text = $group->getOption('description'); - if ($text instanceof Html) { - $s .= $text; - - } elseif (is_string($text)) { - if ($translator !== NULL) { - $text = $translator->translate($text); - } - $s .= $this->getWrapper('group description')->setText($text) . "\n"; - } - - $s .= $this->renderControls($group); - - $remains = $container->endTag() . "\n" . $remains; - if (!$group->getOption('embedNext')) { - $s .= $remains; - $remains = ''; - } - } - - $s .= $remains . $this->renderControls($this->form); - - $container = $this->getWrapper('form container'); - $container->setHtml($s); - return $container->render(0); - } - - - - /** - * Renders group of controls. - * @param Nette\Forms\Container|FormGroup - * @return string - */ - public function renderControls($parent) - { - if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) { - throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance."); - } - - $container = $this->getWrapper('controls container'); - - $buttons = NULL; - foreach ($parent->getControls() as $control) { - if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) { - // skip - - } elseif ($control instanceof Nette\Forms\Controls\Button) { - $buttons[] = $control; - - } else { - if ($buttons) { - $container->add($this->renderPairMulti($buttons)); - $buttons = NULL; - } - $container->add($this->renderPair($control)); - } - } - - if ($buttons) { - $container->add($this->renderPairMulti($buttons)); - } - - $s = ''; - if (count($container)) { - $s .= "\n" . $container . "\n"; - } - - return $s; - } - - - - /** - * Renders single visual row. - * @param Nette\Forms\IControl - * @return string - */ - public function renderPair(Nette\Forms\IControl $control) - { - $pair = $this->getWrapper('pair container'); - $pair->add($this->renderLabel($control)); - $pair->add($this->renderControl($control)); - $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE); - $pair->class($control->getOption('class'), TRUE); - if (++$this->counter % 2) { - $pair->class($this->getValue('pair .odd'), TRUE); - } - $pair->id = $control->getOption('id'); - return $pair->render(0); - } - - - - /** - * Renders single visual row of multiple controls. - * @param array of IFormControl - * @return string - */ - public function renderPairMulti(array $controls) - { - $s = array(); - foreach ($controls as $control) { - if (!$control instanceof Nette\Forms\IControl) { - throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances."); - } - $s[] = (string) $control->getControl(); - } - $pair = $this->getWrapper('pair container'); - $pair->add($this->renderLabel($control)); - $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s))); - return $pair->render(0); - } - - - - /** - * Renders 'label' part of visual row of controls. - * @param Nette\Forms\IControl - * @return string - */ - public function renderLabel(Nette\Forms\IControl $control) - { - $head = $this->getWrapper('label container'); - - if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) { - return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : ''); - - } else { - $label = $control->getLabel(); - $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : ''); - if ($label instanceof Html) { - $label->setHtml($label->getHtml() . $suffix); - $suffix = ''; - } - return $head->setHtml((string) $label . $suffix); - } - } - - - - /** - * Renders 'control' part of visual row of controls. - * @param Nette\Forms\IControl - * @return string - */ - public function renderControl(Nette\Forms\IControl $control) - { - $body = $this->getWrapper('control container'); - if ($this->counter % 2) { - $body->class($this->getValue('control .odd'), TRUE); - } - - $description = $control->getOption('description'); - if ($description instanceof Html) { - $description = ' ' . $control->getOption('description'); - - } elseif (is_string($description)) { - $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description)); - - } else { - $description = ''; - } - - if ($control->isRequired()) { - $description = $this->getValue('control requiredsuffix') . $description; - } - - if ($this->getValue('control errors')) { - $description .= $this->renderErrors($control); - } - - if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) { - return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description); - - } else { - return $body->setHtml((string) $control->getControl() . $description); - } - } - - - - /** - * @param string - * @return Nette\Utils\Html - */ - protected function getWrapper($name) - { - $data = $this->getValue($name); - return $data instanceof Html ? clone $data : Html::el($data); - } - - - - /** - * @param string - * @return string - */ - protected function getValue($name) - { - $name = explode(' ', $name); - $data = & $this->wrappers[$name[0]][$name[1]]; - return $data; - } - -} + array( + 'container' => NULL, + 'errors' => TRUE, + ), + + 'error' => array( + 'container' => 'ul class=error', + 'item' => 'li', + ), + + 'group' => array( + 'container' => 'fieldset', + 'label' => 'legend', + 'description' => 'p', + ), + + 'controls' => array( + 'container' => 'table', + ), + + 'pair' => array( + 'container' => 'tr', + '.required' => 'required', + '.optional' => NULL, + '.odd' => NULL, + ), + + 'control' => array( + 'container' => 'td', + '.odd' => NULL, + + 'errors' => FALSE, + 'description' => 'small', + 'requiredsuffix' => '', + + '.required' => 'required', + '.text' => 'text', + '.password' => 'text', + '.file' => 'text', + '.submit' => 'button', + '.image' => 'imagebutton', + '.button' => 'button', + ), + + 'label' => array( + 'container' => 'th', + 'suffix' => NULL, + 'requiredsuffix' => '', + ), + + 'hidden' => array( + 'container' => 'div', + ), + ); + + /** @var Nette\Forms\Form */ + protected $form; + + /** @var int */ + protected $counter; + + + + /** + * Provides complete form rendering. + * @param Nette\Forms\Form + * @param string 'begin', 'errors', 'body', 'end' or empty to render all + * @return string + */ + public function render(Nette\Forms\Form $form, $mode = NULL) + { + if ($this->form !== $form) { + $this->form = $form; + $this->init(); + } + + $s = ''; + if (!$mode || $mode === 'begin') { + $s .= $this->renderBegin(); + } + if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') { + $s .= $this->renderErrors(); + } + if (!$mode || $mode === 'body') { + $s .= $this->renderBody(); + } + if (!$mode || $mode === 'end') { + $s .= $this->renderEnd(); + } + return $s; + } + + + + /** @deprecated */ + public function setClientScript() + { + trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING); + return $this; + } + + + + /** + * Initializes form. + * @return void + */ + protected function init() + { + // TODO: only for back compatiblity - remove? + $wrapper = & $this->wrappers['control']; + foreach ($this->form->getControls() as $control) { + if ($control->isRequired() && isset($wrapper['.required'])) { + $control->getLabelPrototype()->class($wrapper['.required'], TRUE); + } + + $el = $control->getControlPrototype(); + if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) { + $el->class($wrapper['.' . $el->type], TRUE); + } + } + } + + + + /** + * Renders form begin. + * @return string + */ + public function renderBegin() + { + $this->counter = 0; + + foreach ($this->form->getControls() as $control) { + $control->setOption('rendered', FALSE); + } + + if (strcasecmp($this->form->getMethod(), 'get') === 0) { + $el = clone $this->form->getElementPrototype(); + $url = explode('?', (string) $el->action, 2); + $el->action = $url[0]; + $s = ''; + if (isset($url[1])) { + foreach (preg_split('#[;&]#', $url[1]) as $param) { + $parts = explode('=', $param, 2); + $name = urldecode($parts[0]); + if (!isset($this->form[$name])) { + $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1]))); + } + } + $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s); + } + return $el->startTag() . $s; + + + } else { + return $this->form->getElementPrototype()->startTag(); + } + } + + + + /** + * Renders form end. + * @return string + */ + public function renderEnd() + { + $s = ''; + foreach ($this->form->getControls() as $control) { + if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) { + $s .= (string) $control->getControl(); + } + } + if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) { + $s .= ''; + } + if ($s) { + $s = $this->getWrapper('hidden container')->setHtml($s) . "\n"; + } + + return $s . $this->form->getElementPrototype()->endTag() . "\n"; + } + + + + /** + * Renders validation errors (per form or per control). + * @return string + */ + public function renderErrors(Nette\Forms\IControl $control = NULL) + { + $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors(); + if (count($errors)) { + $ul = $this->getWrapper('error container'); + $li = $this->getWrapper('error item'); + + foreach ($errors as $error) { + $item = clone $li; + if ($error instanceof Html) { + $item->add($error); + } else { + $item->setText($error); + } + $ul->add($item); + } + return "\n" . $ul->render(0); + } + } + + + + /** + * Renders form body. + * @return string + */ + public function renderBody() + { + $s = $remains = ''; + + $defaultContainer = $this->getWrapper('group container'); + $translator = $this->form->getTranslator(); + + foreach ($this->form->getGroups() as $group) { + if (!$group->getControls() || !$group->getOption('visual')) { + continue; + } + + $container = $group->getOption('container', $defaultContainer); + $container = $container instanceof Html ? clone $container : Html::el($container); + + $s .= "\n" . $container->startTag(); + + $text = $group->getOption('label'); + if ($text instanceof Html) { + $s .= $text; + + } elseif (is_string($text)) { + if ($translator !== NULL) { + $text = $translator->translate($text); + } + $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n"; + } + + $text = $group->getOption('description'); + if ($text instanceof Html) { + $s .= $text; + + } elseif (is_string($text)) { + if ($translator !== NULL) { + $text = $translator->translate($text); + } + $s .= $this->getWrapper('group description')->setText($text) . "\n"; + } + + $s .= $this->renderControls($group); + + $remains = $container->endTag() . "\n" . $remains; + if (!$group->getOption('embedNext')) { + $s .= $remains; + $remains = ''; + } + } + + $s .= $remains . $this->renderControls($this->form); + + $container = $this->getWrapper('form container'); + $container->setHtml($s); + return $container->render(0); + } + + + + /** + * Renders group of controls. + * @param Nette\Forms\Container|FormGroup + * @return string + */ + public function renderControls($parent) + { + if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) { + throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance."); + } + + $container = $this->getWrapper('controls container'); + + $buttons = NULL; + foreach ($parent->getControls() as $control) { + if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) { + // skip + + } elseif ($control instanceof Nette\Forms\Controls\Button) { + $buttons[] = $control; + + } else { + if ($buttons) { + $container->add($this->renderPairMulti($buttons)); + $buttons = NULL; + } + $container->add($this->renderPair($control)); + } + } + + if ($buttons) { + $container->add($this->renderPairMulti($buttons)); + } + + $s = ''; + if (count($container)) { + $s .= "\n" . $container . "\n"; + } + + return $s; + } + + + + /** + * Renders single visual row. + * @return string + */ + public function renderPair(Nette\Forms\IControl $control) + { + $pair = $this->getWrapper('pair container'); + $pair->add($this->renderLabel($control)); + $pair->add($this->renderControl($control)); + $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE); + $pair->class($control->getOption('class'), TRUE); + if (++$this->counter % 2) { + $pair->class($this->getValue('pair .odd'), TRUE); + } + $pair->id = $control->getOption('id'); + return $pair->render(0); + } + + + + /** + * Renders single visual row of multiple controls. + * @param IFormControl[] + * @return string + */ + public function renderPairMulti(array $controls) + { + $s = array(); + foreach ($controls as $control) { + if (!$control instanceof Nette\Forms\IControl) { + throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances."); + } + $s[] = (string) $control->getControl(); + } + $pair = $this->getWrapper('pair container'); + $pair->add($this->renderLabel($control)); + $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s))); + return $pair->render(0); + } + + + + /** + * Renders 'label' part of visual row of controls. + * @return string + */ + public function renderLabel(Nette\Forms\IControl $control) + { + $head = $this->getWrapper('label container'); + + if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) { + return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : ''); + + } else { + $label = $control->getLabel(); + $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : ''); + if ($label instanceof Html) { + $label->setHtml($label->getHtml() . $suffix); + $suffix = ''; + } + return $head->setHtml((string) $label . $suffix); + } + } + + + + /** + * Renders 'control' part of visual row of controls. + * @return string + */ + public function renderControl(Nette\Forms\IControl $control) + { + $body = $this->getWrapper('control container'); + if ($this->counter % 2) { + $body->class($this->getValue('control .odd'), TRUE); + } + + $description = $control->getOption('description'); + if ($description instanceof Html) { + $description = ' ' . $control->getOption('description'); + + } elseif (is_string($description)) { + $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description)); + + } else { + $description = ''; + } + + if ($control->isRequired()) { + $description = $this->getValue('control requiredsuffix') . $description; + } + + if ($this->getValue('control errors')) { + $description .= $this->renderErrors($control); + } + + if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) { + return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description); + + } else { + return $body->setHtml((string) $control->getControl() . $description); + } + } + + + + /** + * @param string + * @return Nette\Utils\Html + */ + protected function getWrapper($name) + { + $data = $this->getValue($name); + return $data instanceof Html ? clone $data : Html::el($data); + } + + + + /** + * @param string + * @return string + */ + protected function getValue($name) + { + $name = explode(' ', $name); + $data = & $this->wrappers[$name[0]][$name[1]]; + return $data; + } + +} diff --git a/libs/Nette/Forms/Rule.php b/libs/Nette/Forms/Rule.php index 8f2e0b9..60ee09e 100644 --- a/libs/Nette/Forms/Rule.php +++ b/libs/Nette/Forms/Rule.php @@ -1,55 +1,55 @@ - 'Security token did not match. Possible CSRF attack.', - Form::EQUAL => 'Please enter %s.', - Form::FILLED => 'Please complete mandatory field.', - Form::MIN_LENGTH => 'Please enter a value of at least %d characters.', - Form::MAX_LENGTH => 'Please enter a value no longer than %d characters.', - Form::LENGTH => 'Please enter a value between %d and %d characters long.', - Form::EMAIL => 'Please enter a valid email address.', - Form::URL => 'Please enter a valid URL.', - Form::INTEGER => 'Please enter a numeric value.', - Form::FLOAT => 'Please enter a numeric value.', - Form::RANGE => 'Please enter a value between %d and %d.', - Form::MAX_FILE_SIZE => 'The size of the uploaded file can be up to %d bytes.', - Form::IMAGE => 'The uploaded file must be image in format JPEG, GIF or PNG.', - ); - - /** @var array of Rule */ - private $rules = array(); - - /** @var Rules */ - private $parent; - - /** @var array */ - private $toggles = array(); - - /** @var IControl */ - private $control; - - - - public function __construct(IControl $control) - { - $this->control = $control; - } - - - - /** - * Adds a validation rule for the current control. - * @param mixed rule type - * @param string message to display for invalid data - * @param mixed optional rule arguments - * @return Rules provides a fluent interface - */ - public function addRule($operation, $message = NULL, $arg = NULL) - { - $rule = new Rule; - $rule->control = $this->control; - $rule->operation = $operation; - $this->adjustOperation($rule); - $rule->arg = $arg; - $rule->type = Rule::VALIDATOR; - if ($message === NULL && is_string($rule->operation) && isset(self::$defaultMessages[$rule->operation])) { - $rule->message = self::$defaultMessages[$rule->operation]; - } else { - $rule->message = $message; - } - $this->rules[] = $rule; - return $this; - } - - - - /** - * Adds a validation condition a returns new branch. - * @param mixed condition type - * @param mixed optional condition arguments - * @return Rules new branch - */ - public function addCondition($operation, $arg = NULL) - { - return $this->addConditionOn($this->control, $operation, $arg); - } - - - - /** - * Adds a validation condition on specified control a returns new branch. - * @param IControl form control - * @param mixed condition type - * @param mixed optional condition arguments - * @return Rules new branch - */ - public function addConditionOn(IControl $control, $operation, $arg = NULL) - { - $rule = new Rule; - $rule->control = $control; - $rule->operation = $operation; - $this->adjustOperation($rule); - $rule->arg = $arg; - $rule->type = Rule::CONDITION; - $rule->subRules = new static($this->control); - $rule->subRules->parent = $this; - - $this->rules[] = $rule; - return $rule->subRules; - } - - - - /** - * Adds a else statement. - * @return Rules else branch - */ - public function elseCondition() - { - $rule = clone end($this->parent->rules); - $rule->isNegative = !$rule->isNegative; - $rule->subRules = new static($this->parent->control); - $rule->subRules->parent = $this->parent; - $this->parent->rules[] = $rule; - return $rule->subRules; - } - - - - /** - * Ends current validation condition. - * @return Rules parent branch - */ - public function endCondition() - { - return $this->parent; - } - - - - /** - * Toggles HTML elememnt visibility. - * @param string element id - * @param bool hide element? - * @return Rules provides a fluent interface - */ - public function toggle($id, $hide = TRUE) - { - $this->toggles[$id] = $hide; - return $this; - } - - - - /** - * Validates against ruleset. - * @param bool stop before first error? - * @return bool is valid? - */ - public function validate($onlyCheck = FALSE) - { - foreach ($this->rules as $rule) { - if ($rule->control->isDisabled()) { - continue; - } - - $success = ($rule->isNegative xor $this->getCallback($rule)->invoke($rule->control, $rule->arg)); - - if ($rule->type === Rule::CONDITION && $success) { - if (!$rule->subRules->validate($onlyCheck)) { - return FALSE; - } - - } elseif ($rule->type === Rule::VALIDATOR && !$success) { - if (!$onlyCheck) { - $rule->control->addError(self::formatMessage($rule, TRUE)); - } - return FALSE; - } - } - return TRUE; - } - - - - /** - * Iterates over ruleset. - * @return \ArrayIterator - */ - final public function getIterator() - { - return new \ArrayIterator($this->rules); - } - - - - /** - * @return array - */ - final public function getToggles() - { - return $this->toggles; - } - - - - /** - * Process 'operation' string. - * @param Rule - * @return void - */ - private function adjustOperation($rule) - { - if (is_string($rule->operation) && ord($rule->operation[0]) > 127) { - $rule->isNegative = TRUE; - $rule->operation = ~$rule->operation; - } - - if (!$this->getCallback($rule)->isCallable()) { - $operation = is_scalar($rule->operation) ? " '$rule->operation'" : ''; - throw new Nette\InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'."); - } - } - - - - private function getCallback($rule) - { - $op = $rule->operation; - if (is_string($op) && strncmp($op, ':', 1) === 0) { - return callback(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':')); - } else { - return callback($op); - } - } - - - - public static function formatMessage($rule, $withValue) - { - $message = $rule->message; - if (!isset($message)) { // report missing message by notice - $message = self::$defaultMessages[$rule->operation]; - } - if ($translator = $rule->control->getForm()->getTranslator()) { - $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL); - } - $message = vsprintf(preg_replace('#%(name|label|value)#', '%$0', $message), (array) $rule->arg); - $message = str_replace('%name', $rule->control->getName(), $message); - $message = str_replace('%label', $rule->control->translate($rule->control->caption), $message); - if ($withValue && strpos($message, '%value') !== FALSE) { - $message = str_replace('%value', $rule->control->getValue(), $message); - } - return $message; - } - -} + 'Please submit this form again (security token has expired).', + Form::EQUAL => 'Please enter %s.', + Form::FILLED => 'Please complete mandatory field.', + Form::MIN_LENGTH => 'Please enter a value of at least %d characters.', + Form::MAX_LENGTH => 'Please enter a value no longer than %d characters.', + Form::LENGTH => 'Please enter a value between %d and %d characters long.', + Form::EMAIL => 'Please enter a valid email address.', + Form::URL => 'Please enter a valid URL.', + Form::INTEGER => 'Please enter a numeric value.', + Form::FLOAT => 'Please enter a numeric value.', + Form::RANGE => 'Please enter a value between %d and %d.', + Form::MAX_FILE_SIZE => 'The size of the uploaded file can be up to %d bytes.', + Form::IMAGE => 'The uploaded file must be image in format JPEG, GIF or PNG.', + ); + + /** @var Rule[] */ + private $rules = array(); + + /** @var Rules */ + private $parent; + + /** @var array */ + private $toggles = array(); + + /** @var IControl */ + private $control; + + + + public function __construct(IControl $control) + { + $this->control = $control; + } + + + + /** + * Adds a validation rule for the current control. + * @param mixed rule type + * @param string message to display for invalid data + * @param mixed optional rule arguments + * @return Rules provides a fluent interface + */ + public function addRule($operation, $message = NULL, $arg = NULL) + { + $rule = new Rule; + $rule->control = $this->control; + $rule->operation = $operation; + $this->adjustOperation($rule); + $rule->arg = $arg; + $rule->type = Rule::VALIDATOR; + if ($message === NULL && is_string($rule->operation) && isset(static::$defaultMessages[$rule->operation])) { + $rule->message = static::$defaultMessages[$rule->operation]; + } else { + $rule->message = $message; + } + $this->rules[] = $rule; + return $this; + } + + + + /** + * Adds a validation condition a returns new branch. + * @param mixed condition type + * @param mixed optional condition arguments + * @return Rules new branch + */ + public function addCondition($operation, $arg = NULL) + { + return $this->addConditionOn($this->control, $operation, $arg); + } + + + + /** + * Adds a validation condition on specified control a returns new branch. + * @param IControl form control + * @param mixed condition type + * @param mixed optional condition arguments + * @return Rules new branch + */ + public function addConditionOn(IControl $control, $operation, $arg = NULL) + { + $rule = new Rule; + $rule->control = $control; + $rule->operation = $operation; + $this->adjustOperation($rule); + $rule->arg = $arg; + $rule->type = Rule::CONDITION; + $rule->subRules = new static($this->control); + $rule->subRules->parent = $this; + + $this->rules[] = $rule; + return $rule->subRules; + } + + + + /** + * Adds a else statement. + * @return Rules else branch + */ + public function elseCondition() + { + $rule = clone end($this->parent->rules); + $rule->isNegative = !$rule->isNegative; + $rule->subRules = new static($this->parent->control); + $rule->subRules->parent = $this->parent; + $this->parent->rules[] = $rule; + return $rule->subRules; + } + + + + /** + * Ends current validation condition. + * @return Rules parent branch + */ + public function endCondition() + { + return $this->parent; + } + + + + /** + * Toggles HTML elememnt visibility. + * @param string element id + * @param bool hide element? + * @return Rules provides a fluent interface + */ + public function toggle($id, $hide = TRUE) + { + $this->toggles[$id] = $hide; + return $this; + } + + + + /** + * Validates against ruleset. + * @param bool stop before first error? + * @return bool is valid? + */ + public function validate($onlyCheck = FALSE) + { + foreach ($this->rules as $rule) { + if ($rule->control->isDisabled()) { + continue; + } + + $success = ($rule->isNegative xor $this->getCallback($rule)->invoke($rule->control, $rule->arg)); + + if ($rule->type === Rule::CONDITION && $success) { + if (!$rule->subRules->validate($onlyCheck)) { + return FALSE; + } + + } elseif ($rule->type === Rule::VALIDATOR && !$success) { + if (!$onlyCheck) { + $rule->control->addError(static::formatMessage($rule, TRUE)); + } + return FALSE; + } + } + return TRUE; + } + + + + /** + * Iterates over ruleset. + * @return \ArrayIterator + */ + final public function getIterator() + { + return new \ArrayIterator($this->rules); + } + + + + /** + * @return array + */ + final public function getToggles() + { + return $this->toggles; + } + + + + /** + * Process 'operation' string. + * @param Rule + * @return void + */ + private function adjustOperation($rule) + { + if (is_string($rule->operation) && ord($rule->operation[0]) > 127) { + $rule->isNegative = TRUE; + $rule->operation = ~$rule->operation; + } + + if (!$this->getCallback($rule)->isCallable()) { + $operation = is_scalar($rule->operation) ? " '$rule->operation'" : ''; + throw new Nette\InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'."); + } + } + + + + private function getCallback($rule) + { + $op = $rule->operation; + if (is_string($op) && strncmp($op, ':', 1) === 0) { + return new Nette\Callback(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':')); + } else { + return new Nette\Callback($op); + } + } + + + + public static function formatMessage($rule, $withValue) + { + $message = $rule->message; + if ($message instanceof Nette\Utils\Html) { + return $message; + } + if (!isset($message)) { // report missing message by notice + $message = static::$defaultMessages[$rule->operation]; + } + if ($translator = $rule->control->getForm()->getTranslator()) { + $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL); + } + $message = vsprintf(preg_replace('#%(name|label|value)#', '%$0', $message), (array) $rule->arg); + $message = str_replace('%name', $rule->control->getName(), $message); + $message = str_replace('%label', $rule->control->translate($rule->control->caption), $message); + if ($withValue && strpos($message, '%value') !== FALSE) { + $message = str_replace('%value', $rule->control->getValue(), $message); + } + return $message; + } + +} diff --git a/libs/Nette/Http/Context.php b/libs/Nette/Http/Context.php index d25538b..1ea71bc 100644 --- a/libs/Nette/Http/Context.php +++ b/libs/Nette/Http/Context.php @@ -1,110 +1,114 @@ -request = $request; - $this->response = $response; - } - - - - /** - * Attempts to cache the sent entity by its last modification date. - * @param string|int|DateTime last modified time - * @param string strong entity tag validator - * @return bool - */ - public function isModified($lastModified = NULL, $etag = NULL) - { - if ($lastModified) { - $this->response->setHeader('Last-Modified', $this->response->date($lastModified)); - } - if ($etag) { - $this->response->setHeader('ETag', '"' . addslashes($etag) . '"'); - } - - $ifNoneMatch = $this->request->getHeader('If-None-Match'); - if ($ifNoneMatch === '*') { - $match = TRUE; // match, check if-modified-since - - } elseif ($ifNoneMatch !== NULL) { - $etag = $this->response->getHeader('ETag'); - - if ($etag == NULL || strpos(' ' . strtr($ifNoneMatch, ",\t", ' '), ' ' . $etag) === FALSE) { - return TRUE; - - } else { - $match = TRUE; // match, check if-modified-since - } - } - - $ifModifiedSince = $this->request->getHeader('If-Modified-Since'); - if ($ifModifiedSince !== NULL) { - $lastModified = $this->response->getHeader('Last-Modified'); - if ($lastModified != NULL && strtotime($lastModified) <= strtotime($ifModifiedSince)) { - $match = TRUE; - - } else { - return TRUE; - } - } - - if (empty($match)) { - return TRUE; - } - - $this->response->setCode(IResponse::S304_NOT_MODIFIED); - return FALSE; - } - - - - /** - * @return IRequest - */ - public function getRequest() - { - return $this->request; - } - - - - /** - * @return IResponse - */ - public function getResponse() - { - return $this->response; - } - -} +request = $request; + $this->response = $response; + } + + + + /** + * Attempts to cache the sent entity by its last modification date. + * @param string|int|DateTime last modified time + * @param string strong entity tag validator + * @return bool + */ + public function isModified($lastModified = NULL, $etag = NULL) + { + if ($lastModified) { + $this->response->setHeader('Last-Modified', $this->response->date($lastModified)); + } + if ($etag) { + $this->response->setHeader('ETag', '"' . addslashes($etag) . '"'); + } + + $ifNoneMatch = $this->request->getHeader('If-None-Match'); + if ($ifNoneMatch === '*') { + $match = TRUE; // match, check if-modified-since + + } elseif ($ifNoneMatch !== NULL) { + $etag = $this->response->getHeader('ETag'); + + if ($etag == NULL || strpos(' ' . strtr($ifNoneMatch, ",\t", ' '), ' ' . $etag) === FALSE) { + return TRUE; + + } else { + $match = TRUE; // match, check if-modified-since + } + } + + $ifModifiedSince = $this->request->getHeader('If-Modified-Since'); + if ($ifModifiedSince !== NULL) { + $lastModified = $this->response->getHeader('Last-Modified'); + if ($lastModified != NULL && strtotime($lastModified) <= strtotime($ifModifiedSince)) { + $match = TRUE; + + } else { + return TRUE; + } + } + + if (empty($match)) { + return TRUE; + } + + $this->response->setCode(IResponse::S304_NOT_MODIFIED); + return FALSE; + } + + + + /** + * @return IRequest + */ + public function getRequest() + { + return $this->request; + } + + + + /** + * @return IResponse + */ + public function getResponse() + { + return $this->response; + } + +} diff --git a/libs/Nette/Http/FileUpload.php b/libs/Nette/Http/FileUpload.php index 5bac7f2..480511f 100644 --- a/libs/Nette/Http/FileUpload.php +++ b/libs/Nette/Http/FileUpload.php @@ -1,212 +1,222 @@ -error = UPLOAD_ERR_NO_FILE; - return; // or throw exception? - } - } - $this->name = $value['name']; - $this->size = $value['size']; - $this->tmpName = $value['tmp_name']; - $this->error = $value['error']; - } - - - - /** - * Returns the file name. - * @return string - */ - public function getName() - { - return $this->name; - } - - - - /** - * Returns the MIME content type of an uploaded file. - * @return string - */ - public function getContentType() - { - if ($this->isOk() && $this->type === NULL) { - $this->type = Nette\Utils\MimeTypeDetector::fromFile($this->tmpName); - } - return $this->type; - } - - - - /** - * Returns the size of an uploaded file. - * @return int - */ - public function getSize() - { - return $this->size; - } - - - - /** - * Returns the path to an uploaded file. - * @return string - */ - public function getTemporaryFile() - { - return $this->tmpName; - } - - - - /** - * Returns the path to an uploaded file. - * @return string - */ - public function __toString() - { - return $this->tmpName; - } - - - - /** - * Returns the error code. {@link http://php.net/manual/en/features.file-upload.errors.php} - * @return int - */ - public function getError() - { - return $this->error; - } - - - - /** - * Is there any error? - * @return bool - */ - public function isOk() - { - return $this->error === UPLOAD_ERR_OK; - } - - - - /** - * Move uploaded file to new location. - * @param string - * @return FileUpload provides a fluent interface - */ - public function move($dest) - { - $dir = dirname($dest); - if (@mkdir($dir, 0755, TRUE)) { // @ - $dir may already exist - chmod($dir, 0755); - } - $func = is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename'; - if (!$func($this->tmpName, $dest)) { - throw new Nette\InvalidStateException("Unable to move uploaded file '$this->tmpName' to '$dest'."); - } - chmod($dest, 0644); - $this->tmpName = $dest; - return $this; - } - - - - /** - * Is uploaded file GIF, PNG or JPEG? - * @return bool - */ - public function isImage() - { - return in_array($this->getContentType(), array('image/gif', 'image/png', 'image/jpeg'), TRUE); - } - - - - /** - * Returns the image. - * @return Nette\Image - */ - public function toImage() - { - return Nette\Image::fromFile($this->tmpName); - } - - - - /** - * Returns the dimensions of an uploaded image as array. - * @return array - */ - public function getImageSize() - { - return $this->isOk() ? @getimagesize($this->tmpName) : NULL; // @ - files smaller than 12 bytes causes read error - } - - - - /** - * Get file contents. - * @return string - */ - public function getContents() - { - // future implementation can try to work around safe_mode and open_basedir limitations - return $this->isOk() ? file_get_contents($this->tmpName) : NULL; - } - -} +error = UPLOAD_ERR_NO_FILE; + return; // or throw exception? + } + } + $this->name = $value['name']; + $this->size = $value['size']; + $this->tmpName = $value['tmp_name']; + $this->error = $value['error']; + } + + + + /** + * Returns the file name. + * @return string + */ + public function getName() + { + return $this->name; + } + + + + /** + * Returns the sanitized file name. + * @return string + */ + public function getSanitizedName() + { + return trim(Nette\Utils\Strings::webalize($this->name, '.', FALSE), '.-'); + } + + + + /** + * Returns the MIME content type of an uploaded file. + * @return string + */ + public function getContentType() + { + if ($this->isOk() && $this->type === NULL) { + $this->type = Nette\Utils\MimeTypeDetector::fromFile($this->tmpName); + } + return $this->type; + } + + + + /** + * Returns the size of an uploaded file. + * @return int + */ + public function getSize() + { + return $this->size; + } + + + + /** + * Returns the path to an uploaded file. + * @return string + */ + public function getTemporaryFile() + { + return $this->tmpName; + } + + + + /** + * Returns the path to an uploaded file. + * @return string + */ + public function __toString() + { + return $this->tmpName; + } + + + + /** + * Returns the error code. {@link http://php.net/manual/en/features.file-upload.errors.php} + * @return int + */ + public function getError() + { + return $this->error; + } + + + + /** + * Is there any error? + * @return bool + */ + public function isOk() + { + return $this->error === UPLOAD_ERR_OK; + } + + + + /** + * Move uploaded file to new location. + * @param string + * @return FileUpload provides a fluent interface + */ + public function move($dest) + { + @mkdir(dirname($dest), 0777, TRUE); // @ - dir may already exist + /*5.2*if (substr(PHP_OS, 0, 3) === 'WIN') { @unlink($dest); }*/ + if (!call_user_func(is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename', $this->tmpName, $dest)) { + throw new Nette\InvalidStateException("Unable to move uploaded file '$this->tmpName' to '$dest'."); + } + chmod($dest, 0666); + $this->tmpName = $dest; + return $this; + } + + + + /** + * Is uploaded file GIF, PNG or JPEG? + * @return bool + */ + public function isImage() + { + return in_array($this->getContentType(), array('image/gif', 'image/png', 'image/jpeg'), TRUE); + } + + + + /** + * Returns the image. + * @return Nette\Image + */ + public function toImage() + { + return Nette\Image::fromFile($this->tmpName); + } + + + + /** + * Returns the dimensions of an uploaded image as array. + * @return array + */ + public function getImageSize() + { + return $this->isOk() ? @getimagesize($this->tmpName) : NULL; // @ - files smaller than 12 bytes causes read error + } + + + + /** + * Get file contents. + * @return string + */ + public function getContents() + { + // future implementation can try to work around safe_mode and open_basedir limitations + return $this->isOk() ? file_get_contents($this->tmpName) : NULL; + } + +} diff --git a/libs/Nette/Http/IRequest.php b/libs/Nette/Http/IRequest.php index 1fc7522..0ccb123 100644 --- a/libs/Nette/Http/IRequest.php +++ b/libs/Nette/Http/IRequest.php @@ -1,140 +1,140 @@ -url = $url; - $this->url->freeze(); - if ($query === NULL) { - parse_str($url->query, $this->query); - } else { - $this->query = (array) $query; - } - $this->post = (array) $post; - $this->files = (array) $files; - $this->cookies = (array) $cookies; - $this->headers = (array) $headers; - $this->method = $method; - $this->remoteAddress = $remoteAddress; - $this->remoteHost = $remoteHost; - } - - - - /** - * Returns URL object. - * @return UrlScript - */ - final public function getUrl() - { - return $this->url; - } - - - - /** @deprecated */ - function getUri() - { - trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getUrl() instead.', E_USER_WARNING); - return $this->getUrl(); - } - - - - /********************* query, post, files & cookies ****************d*g**/ - - - - /** - * Returns variable provided to the script via URL query ($_GET). - * If no key is passed, returns the entire array. - * @param string key - * @param mixed default value - * @return mixed - */ - final public function getQuery($key = NULL, $default = NULL) - { - if (func_num_args() === 0) { - return $this->query; - - } elseif (isset($this->query[$key])) { - return $this->query[$key]; - - } else { - return $default; - } - } - - - - /** - * Returns variable provided to the script via POST method ($_POST). - * If no key is passed, returns the entire array. - * @param string key - * @param mixed default value - * @return mixed - */ - final public function getPost($key = NULL, $default = NULL) - { - if (func_num_args() === 0) { - return $this->post; - - } elseif (isset($this->post[$key])) { - return $this->post[$key]; - - } else { - return $default; - } - } - - - - /** - * Returns uploaded file. - * @param string key (or more keys) - * @return FileUpload - */ - final public function getFile($key) - { - $args = func_get_args(); - return Nette\Utils\Arrays::get($this->files, $args); - } - - - - /** - * Returns uploaded files. - * @return array - */ - final public function getFiles() - { - return $this->files; - } - - - - /** - * Returns variable provided to the script via HTTP cookies. - * @param string key - * @param mixed default value - * @return mixed - */ - final public function getCookie($key, $default = NULL) - { - if (func_num_args() === 0) { - return $this->cookies; - - } elseif (isset($this->cookies[$key])) { - return $this->cookies[$key]; - - } else { - return $default; - } - } - - - - /** - * Returns variables provided to the script via HTTP cookies. - * @return array - */ - final public function getCookies() - { - return $this->cookies; - } - - - - /********************* method & headers ****************d*g**/ - - - - /** - * Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive. - * @return string - */ - public function getMethod() - { - return $this->method; - } - - - - /** - * Checks if the request method is the given one. - * @param string - * @return bool - */ - public function isMethod($method) - { - return strcasecmp($this->method, $method) === 0; - } - - - - /** - * Checks if the request method is POST. - * @return bool - */ - public function isPost() - { - return $this->isMethod('POST'); - } - - - - /** - * Return the value of the HTTP header. Pass the header name as the - * plain, HTTP-specified header name (e.g. 'Accept-Encoding'). - * @param string - * @param mixed - * @return mixed - */ - final public function getHeader($header, $default = NULL) - { - $header = strtolower($header); - if (isset($this->headers[$header])) { - return $this->headers[$header]; - } else { - return $default; - } - } - - - - /** - * Returns all HTTP headers. - * @return array - */ - public function getHeaders() - { - return $this->headers; - } - - - - /** - * Returns referrer. - * @return Url|NULL - */ - final public function getReferer() - { - return isset($this->headers['referer']) ? new Url($this->headers['referer']) : NULL; - } - - - - /** - * Is the request is sent via secure channel (https). - * @return bool - */ - public function isSecured() - { - return $this->url->scheme === 'https'; - } - - - - /** - * Is AJAX request? - * @return bool - */ - public function isAjax() - { - return $this->getHeader('X-Requested-With') === 'XMLHttpRequest'; - } - - - - /** - * Returns the IP address of the remote client. - * @return string - */ - public function getRemoteAddress() - { - return $this->remoteAddress; - } - - - - /** - * Returns the host of the remote client. - * @return string - */ - public function getRemoteHost() - { - if (!$this->remoteHost) { - $this->remoteHost = $this->remoteAddress ? getHostByAddr($this->remoteAddress) : NULL; - } - return $this->remoteHost; - } - - - - /** - * Parse Accept-Language header and returns prefered language. - * @param array Supported languages - * @return string - */ - public function detectLanguage(array $langs) - { - $header = $this->getHeader('Accept-Language'); - if (!$header) { - return NULL; - } - - $s = strtolower($header); // case insensitive - $s = strtr($s, '_', '-'); // cs_CZ means cs-CZ - rsort($langs); // first more specific - preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches); - - if (!$matches[0]) { - return NULL; - } - - $max = 0; - $lang = NULL; - foreach ($matches[1] as $key => $value) { - $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key]; - if ($q > $max) { - $max = $q; $lang = $value; - } - } - - return $lang; - } - -} +url = $url; + $this->url->freeze(); + if ($query === NULL) { + parse_str($url->query, $this->query); + } else { + $this->query = (array) $query; + } + $this->post = (array) $post; + $this->files = (array) $files; + $this->cookies = (array) $cookies; + $this->headers = (array) $headers; + $this->method = $method; + $this->remoteAddress = $remoteAddress; + $this->remoteHost = $remoteHost; + } + + + + /** + * Returns URL object. + * @return UrlScript + */ + final public function getUrl() + { + return $this->url; + } + + + + /** @deprecated */ + function getUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getUrl() instead.', E_USER_WARNING); + return $this->getUrl(); + } + + + + /********************* query, post, files & cookies ****************d*g**/ + + + + /** + * Returns variable provided to the script via URL query ($_GET). + * If no key is passed, returns the entire array. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getQuery($key = NULL, $default = NULL) + { + if (func_num_args() === 0) { + return $this->query; + + } elseif (isset($this->query[$key])) { + return $this->query[$key]; + + } else { + return $default; + } + } + + + + /** + * Returns variable provided to the script via POST method ($_POST). + * If no key is passed, returns the entire array. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getPost($key = NULL, $default = NULL) + { + if (func_num_args() === 0) { + return $this->post; + + } elseif (isset($this->post[$key])) { + return $this->post[$key]; + + } else { + return $default; + } + } + + + + /** + * Returns uploaded file. + * @param string key (or more keys) + * @return FileUpload + */ + final public function getFile($key) + { + $args = func_get_args(); + return Nette\Utils\Arrays::get($this->files, $args, NULL); + } + + + + /** + * Returns uploaded files. + * @return array + */ + final public function getFiles() + { + return $this->files; + } + + + + /** + * Returns variable provided to the script via HTTP cookies. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getCookie($key, $default = NULL) + { + if (func_num_args() === 0) { + return $this->cookies; + + } elseif (isset($this->cookies[$key])) { + return $this->cookies[$key]; + + } else { + return $default; + } + } + + + + /** + * Returns variables provided to the script via HTTP cookies. + * @return array + */ + final public function getCookies() + { + return $this->cookies; + } + + + + /********************* method & headers ****************d*g**/ + + + + /** + * Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive. + * @return string + */ + public function getMethod() + { + return $this->method; + } + + + + /** + * Checks if the request method is the given one. + * @param string + * @return bool + */ + public function isMethod($method) + { + return strcasecmp($this->method, $method) === 0; + } + + + + /** + * Checks if the request method is POST. + * @return bool + */ + public function isPost() + { + return $this->isMethod('POST'); + } + + + + /** + * Return the value of the HTTP header. Pass the header name as the + * plain, HTTP-specified header name (e.g. 'Accept-Encoding'). + * @param string + * @param mixed + * @return mixed + */ + final public function getHeader($header, $default = NULL) + { + $header = strtolower($header); + if (isset($this->headers[$header])) { + return $this->headers[$header]; + } else { + return $default; + } + } + + + + /** + * Returns all HTTP headers. + * @return array + */ + public function getHeaders() + { + return $this->headers; + } + + + + /** + * Returns referrer. + * @return Url|NULL + */ + final public function getReferer() + { + return isset($this->headers['referer']) ? new Url($this->headers['referer']) : NULL; + } + + + + /** + * Is the request is sent via secure channel (https). + * @return bool + */ + public function isSecured() + { + return $this->url->scheme === 'https'; + } + + + + /** + * Is AJAX request? + * @return bool + */ + public function isAjax() + { + return $this->getHeader('X-Requested-With') === 'XMLHttpRequest'; + } + + + + /** + * Returns the IP address of the remote client. + * @return string + */ + public function getRemoteAddress() + { + return $this->remoteAddress; + } + + + + /** + * Returns the host of the remote client. + * @return string + */ + public function getRemoteHost() + { + if (!$this->remoteHost) { + $this->remoteHost = $this->remoteAddress ? getHostByAddr($this->remoteAddress) : NULL; + } + return $this->remoteHost; + } + + + + /** + * Parse Accept-Language header and returns prefered language. + * @param array Supported languages + * @return string + */ + public function detectLanguage(array $langs) + { + $header = $this->getHeader('Accept-Language'); + if (!$header) { + return NULL; + } + + $s = strtolower($header); // case insensitive + $s = strtr($s, '_', '-'); // cs_CZ means cs-CZ + rsort($langs); // first more specific + preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches); + + if (!$matches[0]) { + return NULL; + } + + $max = 0; + $lang = NULL; + foreach ($matches[1] as $key => $value) { + $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key]; + if ($q > $max) { + $max = $q; $lang = $value; + } + } + + return $lang; + } + +} diff --git a/libs/Nette/Http/RequestFactory.php b/libs/Nette/Http/RequestFactory.php index a6cd081..96d10a8 100644 --- a/libs/Nette/Http/RequestFactory.php +++ b/libs/Nette/Http/RequestFactory.php @@ -1,252 +1,252 @@ - array('#/{2,}#' => '/'), // '%20' => '' - 'url' => array(), // '#[.,)]$#' => '' - ); - - /** @var string */ - private $encoding; - - - - /** - * @param string - * @return RequestFactory provides a fluent interface - */ - public function setEncoding($encoding) - { - $this->encoding = $encoding; - return $this; - } - - - - /** - * Creates current HttpRequest object. - * @return Request - */ - public function createHttpRequest() - { - // DETECTS URI, base path and script path of the request. - $url = new UrlScript; - $url->scheme = isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http'; - $url->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; - $url->password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; - - // host & port - if (isset($_SERVER['HTTP_HOST'])) { - $pair = explode(':', $_SERVER['HTTP_HOST']); - - } elseif (isset($_SERVER['SERVER_NAME'])) { - $pair = explode(':', $_SERVER['SERVER_NAME']); - - } else { - $pair = array(''); - } - - $url->host = preg_match('#^[-._a-z0-9]+$#', $pair[0]) ? $pair[0] : ''; - - if (isset($pair[1])) { - $url->port = (int) $pair[1]; - - } elseif (isset($_SERVER['SERVER_PORT'])) { - $url->port = (int) $_SERVER['SERVER_PORT']; - } - - // path & query - if (isset($_SERVER['REQUEST_URI'])) { // Apache, IIS 6.0 - $requestUrl = $_SERVER['REQUEST_URI']; - - } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 (PHP as CGI ?) - $requestUrl = $_SERVER['ORIG_PATH_INFO']; - if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') { - $requestUrl .= '?' . $_SERVER['QUERY_STRING']; - } - } else { - $requestUrl = ''; - } - - $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']); - $tmp = explode('?', $requestUrl, 2); - $url->path = Strings::replace($tmp[0], $this->urlFilters['path']); - $url->query = isset($tmp[1]) ? $tmp[1] : ''; - - // normalized url - $url->canonicalize(); - $url->path = Strings::fixEncoding($url->path); - - // detect script path - if (isset($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME']) - && strncmp($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])) === 0 - ) { - $script = '/' . ltrim(strtr(substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])), '\\', '/'), '/'); - } elseif (isset($_SERVER['SCRIPT_NAME'])) { - $script = $_SERVER['SCRIPT_NAME']; - } else { - $script = '/'; - } - - if (strncasecmp($url->path . '/', $script . '/', strlen($script) + 1) === 0) { // whole script in URL - $url->scriptPath = substr($url->path, 0, strlen($script)); - - } elseif (strncasecmp($url->path, $script, strrpos($script, '/') + 1) === 0) { // directory part of script in URL - $url->scriptPath = substr($url->path, 0, strrpos($script, '/') + 1); - - } else { - $url->scriptPath = '/'; - } - - - // GET, POST, COOKIE - $useFilter = (!in_array(ini_get('filter.default'), array('', 'unsafe_raw')) || ini_get('filter.default_flags')); - - parse_str($url->query, $query); - if (!$query) { - $query = $useFilter ? filter_input_array(INPUT_GET, FILTER_UNSAFE_RAW) : (empty($_GET) ? array() : $_GET); - } - $post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST); - $cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE); - - $gpc = (bool) get_magic_quotes_gpc(); - $old = error_reporting(error_reporting() ^ E_NOTICE); - - // remove fucking quotes and check (and optionally convert) encoding - if ($gpc || $this->encoding) { - $utf = strcasecmp($this->encoding, 'UTF-8') === 0; - $list = array(& $query, & $post, & $cookies); - while (list($key, $val) = each($list)) { - foreach ($val as $k => $v) { - unset($list[$key][$k]); - - if ($gpc) { - $k = stripslashes($k); - } - - if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { - // invalid key -> ignore - - } elseif (is_array($v)) { - $list[$key][$k] = $v; - $list[] = & $list[$key][$k]; - - } else { - if ($gpc && !$useFilter) { - $v = stripSlashes($v); - } - if ($this->encoding) { - if ($utf) { - $v = Strings::fixEncoding($v); - - } else { - if (!Strings::checkEncoding($v)) { - $v = iconv($this->encoding, 'UTF-8//IGNORE', $v); - } - $v = html_entity_decode($v, ENT_QUOTES, 'UTF-8'); - } - $v = preg_replace(self::NONCHARS, '', $v); - } - $list[$key][$k] = $v; - } - } - } - unset($list, $key, $val, $k, $v); - } - - - // FILES and create HttpUploadedFile objects - $files = array(); - $list = array(); - if (!empty($_FILES)) { - foreach ($_FILES as $k => $v) { - if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { - continue; - } - $v['@'] = & $files[$k]; - $list[] = $v; - } - } - - while (list(, $v) = each($list)) { - if (!isset($v['name'])) { - continue; - - } elseif (!is_array($v['name'])) { - if ($gpc) { - $v['name'] = stripSlashes($v['name']); - } - if ($this->encoding) { - $v['name'] = preg_replace(self::NONCHARS, '', Strings::fixEncoding($v['name'])); - } - $v['@'] = new FileUpload($v); - continue; - } - - foreach ($v['name'] as $k => $foo) { - if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { - continue; - } - $list[] = array( - 'name' => $v['name'][$k], - 'type' => $v['type'][$k], - 'size' => $v['size'][$k], - 'tmp_name' => $v['tmp_name'][$k], - 'error' => $v['error'][$k], - '@' => & $v['@'][$k], - ); - } - } - - error_reporting($old); - - - // HEADERS - if (function_exists('apache_request_headers')) { - $headers = array_change_key_case(apache_request_headers(), CASE_LOWER); - } else { - $headers = array(); - foreach ($_SERVER as $k => $v) { - if (strncmp($k, 'HTTP_', 5) == 0) { - $k = substr($k, 5); - } elseif (strncmp($k, 'CONTENT_', 8)) { - continue; - } - $headers[ strtr(strtolower($k), '_', '-') ] = $v; - } - } - - return new Request($url, $query, $post, $files, $cookies, $headers, - isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL, - isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL, - isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL - ); - } - -} + array('#/{2,}#' => '/'), // '%20' => '' + 'url' => array(), // '#[.,)]$#' => '' + ); + + /** @var string */ + private $encoding; + + + + /** + * @param string + * @return RequestFactory provides a fluent interface + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + return $this; + } + + + + /** + * Creates current HttpRequest object. + * @return Request + */ + public function createHttpRequest() + { + // DETECTS URI, base path and script path of the request. + $url = new UrlScript; + $url->scheme = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http'; + $url->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; + $url->password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; + + // host & port + if (isset($_SERVER['HTTP_HOST'])) { + $pair = explode(':', $_SERVER['HTTP_HOST']); + + } elseif (isset($_SERVER['SERVER_NAME'])) { + $pair = explode(':', $_SERVER['SERVER_NAME']); + + } else { + $pair = array(''); + } + + $url->host = preg_match('#^[-._a-z0-9]+$#', $pair[0]) ? $pair[0] : ''; + + if (isset($pair[1])) { + $url->port = (int) $pair[1]; + + } elseif (isset($_SERVER['SERVER_PORT'])) { + $url->port = (int) $_SERVER['SERVER_PORT']; + } + + // path & query + if (isset($_SERVER['REQUEST_URI'])) { // Apache, IIS 6.0 + $requestUrl = $_SERVER['REQUEST_URI']; + + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 (PHP as CGI ?) + $requestUrl = $_SERVER['ORIG_PATH_INFO']; + if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') { + $requestUrl .= '?' . $_SERVER['QUERY_STRING']; + } + } else { + $requestUrl = ''; + } + + $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']); + $tmp = explode('?', $requestUrl, 2); + $url->path = Strings::replace($tmp[0], $this->urlFilters['path']); + $url->query = isset($tmp[1]) ? $tmp[1] : ''; + + // normalized url + $url->canonicalize(); + $url->path = Strings::fixEncoding($url->path); + + // detect script path + if (isset($_SERVER['SCRIPT_NAME'])) { + $script = $_SERVER['SCRIPT_NAME']; + } elseif (isset($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME']) + && strncmp($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])) === 0 + ) { + $script = '/' . ltrim(strtr(substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])), '\\', '/'), '/'); + } else { + $script = '/'; + } + + $path = strtolower($url->path) . '/'; + $script = strtolower($script) . '/'; + $max = min(strlen($path), strlen($script)); + for ($i = 0; $i < $max; $i++) { + if ($path[$i] !== $script[$i]) { + break; + } elseif ($path[$i] === '/') { + $url->scriptPath = substr($url->path, 0, $i + 1); + } + } + + // GET, POST, COOKIE + $useFilter = (!in_array(ini_get('filter.default'), array('', 'unsafe_raw')) || ini_get('filter.default_flags')); + + parse_str($url->query, $query); + if (!$query) { + $query = $useFilter ? filter_input_array(INPUT_GET, FILTER_UNSAFE_RAW) : (empty($_GET) ? array() : $_GET); + } + $post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST); + $cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE); + + $gpc = (bool) get_magic_quotes_gpc(); + $old = error_reporting(error_reporting() ^ E_NOTICE); + + // remove fucking quotes and check (and optionally convert) encoding + if ($gpc || $this->encoding) { + $utf = strcasecmp($this->encoding, 'UTF-8') === 0; + $list = array(& $query, & $post, & $cookies); + while (list($key, $val) = each($list)) { + foreach ($val as $k => $v) { + unset($list[$key][$k]); + + if ($gpc) { + $k = stripslashes($k); + } + + if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { + // invalid key -> ignore + + } elseif (is_array($v)) { + $list[$key][$k] = $v; + $list[] = & $list[$key][$k]; + + } else { + if ($gpc && !$useFilter) { + $v = stripSlashes($v); + } + if ($this->encoding) { + if ($utf) { + $v = Strings::fixEncoding($v); + + } else { + if (!Strings::checkEncoding($v)) { + $v = iconv($this->encoding, 'UTF-8//IGNORE', $v); + } + $v = html_entity_decode($v, ENT_QUOTES, 'UTF-8'); + } + $v = preg_replace(self::NONCHARS, '', $v); + } + $list[$key][$k] = $v; + } + } + } + unset($list, $key, $val, $k, $v); + } + + + // FILES and create FileUpload objects + $files = array(); + $list = array(); + if (!empty($_FILES)) { + foreach ($_FILES as $k => $v) { + if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { + continue; + } + $v['@'] = & $files[$k]; + $list[] = $v; + } + } + + while (list(, $v) = each($list)) { + if (!isset($v['name'])) { + continue; + + } elseif (!is_array($v['name'])) { + if ($gpc) { + $v['name'] = stripSlashes($v['name']); + } + if ($this->encoding) { + $v['name'] = preg_replace(self::NONCHARS, '', Strings::fixEncoding($v['name'])); + } + $v['@'] = new FileUpload($v); + continue; + } + + foreach ($v['name'] as $k => $foo) { + if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { + continue; + } + $list[] = array( + 'name' => $v['name'][$k], + 'type' => $v['type'][$k], + 'size' => $v['size'][$k], + 'tmp_name' => $v['tmp_name'][$k], + 'error' => $v['error'][$k], + '@' => & $v['@'][$k], + ); + } + } + + error_reporting($old); + + + // HEADERS + if (function_exists('apache_request_headers')) { + $headers = array_change_key_case(apache_request_headers(), CASE_LOWER); + } else { + $headers = array(); + foreach ($_SERVER as $k => $v) { + if (strncmp($k, 'HTTP_', 5) == 0) { + $k = substr($k, 5); + } elseif (strncmp($k, 'CONTENT_', 8)) { + continue; + } + $headers[ strtr(strtolower($k), '_', '-') ] = $v; + } + } + + return new Request($url, $query, $post, $files, $cookies, $headers, + isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL, + isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL, + isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL + ); + } + +} diff --git a/libs/Nette/Http/Response.php b/libs/Nette/Http/Response.php index d8c64ce..648cd01 100644 --- a/libs/Nette/Http/Response.php +++ b/libs/Nette/Http/Response.php @@ -1,330 +1,341 @@ -1, 201=>1, 202=>1, 203=>1, 204=>1, 205=>1, 206=>1, - 300=>1, 301=>1, 302=>1, 303=>1, 304=>1, 307=>1, - 400=>1, 401=>1, 403=>1, 404=>1, 406=>1, 408=>1, 410=>1, 412=>1, 415=>1, 416=>1, - 500=>1, 501=>1, 503=>1, 505=>1 - ); - - if (!isset($allowed[$code])) { - throw new Nette\InvalidArgumentException("Bad HTTP response '$code'."); - - } elseif (headers_sent($file, $line)) { - throw new Nette\InvalidStateException("Cannot set HTTP code after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); - - } else { - $this->code = $code; - $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; - header($protocol . ' ' . $code, TRUE, $code); - } - return $this; - } - - - - /** - * Returns HTTP response code. - * @return int - */ - public function getCode() - { - return $this->code; - } - - - - /** - * Sends a HTTP header and replaces a previous one. - * @param string header name - * @param string header value - * @return Response provides a fluent interface - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function setHeader($name, $value) - { - if (headers_sent($file, $line)) { - throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); - } - - if ($value === NULL && function_exists('header_remove')) { - header_remove($name); - } else { - header($name . ': ' . $value, TRUE, $this->code); - } - return $this; - } - - - - /** - * Adds HTTP header. - * @param string header name - * @param string header value - * @return void - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function addHeader($name, $value) - { - if (headers_sent($file, $line)) { - throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); - } - - header($name . ': ' . $value, FALSE, $this->code); - } - - - - /** - * Sends a Content-type HTTP header. - * @param string mime-type - * @param string charset - * @return Response provides a fluent interface - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function setContentType($type, $charset = NULL) - { - $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); - return $this; - } - - - - /** - * Redirects to a new URL. Note: call exit() after it. - * @param string URL - * @param int HTTP code - * @return void - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function redirect($url, $code = self::S302_FOUND) - { - if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('#^Microsoft-IIS/[1-5]#', $_SERVER['SERVER_SOFTWARE']) - && $this->getHeader('Set-Cookie') !== NULL - ) { - $this->setHeader('Refresh', "0;url=$url"); - return; - } - - $this->setCode($code); - $this->setHeader('Location', $url); - echo "

    Redirect

    \n\n

    Please click here to continue.

    "; - } - - - - /** - * Sets the number of seconds before a page cached on a browser expires. - * @param string|int|DateTime time, value 0 means "until the browser is closed" - * @return Response provides a fluent interface - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function setExpiration($time) - { - if (!$time) { // no cache - $this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate'); - $this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT'); - return $this; - } - - $time = Nette\DateTime::from($time); - $this->setHeader('Cache-Control', 'max-age=' . ($time->format('U') - time())); - $this->setHeader('Expires', self::date($time)); - return $this; - } - - - - /** - * Checks if headers have been sent. - * @return bool - */ - public function isSent() - { - return headers_sent(); - } - - - - /** - * Return the value of the HTTP header. - * @param string - * @param mixed - * @return mixed - */ - public function getHeader($header, $default = NULL) - { - $header .= ':'; - $len = strlen($header); - foreach (headers_list() as $item) { - if (strncasecmp($item, $header, $len) === 0) { - return ltrim(substr($item, $len)); - } - } - return $default; - } - - - - /** - * Returns a list of headers to sent. - * @return array - */ - public function getHeaders() - { - $headers = array(); - foreach (headers_list() as $header) { - $a = strpos($header, ':'); - $headers[substr($header, 0, $a)] = (string) substr($header, $a + 2); - } - return $headers; - } - - - - /** - * Returns HTTP valid date format. - * @param string|int|DateTime - * @return string - */ - public static function date($time = NULL) - { - $time = Nette\DateTime::from($time); - $time->setTimezone(new \DateTimeZone('GMT')); - return $time->format('D, d M Y H:i:s \G\M\T'); - } - - - - /** - * @return void - */ - public function __destruct() - { - if (self::$fixIE && isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE - && in_array($this->code, array(400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505), TRUE) - && $this->getHeader('Content-Type', 'text/html') === 'text/html' - ) { - echo Nette\Utils\Strings::random(2e3, " \t\r\n"); // sends invisible garbage for IE - self::$fixIE = FALSE; - } - } - - - - /** - * Sends a cookie. - * @param string name of the cookie - * @param string value - * @param string|int|DateTime expiration time, value 0 means "until the browser is closed" - * @param string - * @param string - * @param bool - * @param bool - * @return Response provides a fluent interface - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function setCookie($name, $value, $time, $path = NULL, $domain = NULL, $secure = NULL, $httpOnly = NULL) - { - if (headers_sent($file, $line)) { - throw new Nette\InvalidStateException("Cannot set cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); - } - - setcookie( - $name, - $value, - $time ? Nette\DateTime::from($time)->format('U') : 0, - $path === NULL ? $this->cookiePath : (string) $path, - $domain === NULL ? $this->cookieDomain : (string) $domain, - $secure === NULL ? $this->cookieSecure : (bool) $secure, - $httpOnly === NULL ? $this->cookieHttpOnly : (bool) $httpOnly - ); - return $this; - } - - - - /** - * Deletes a cookie. - * @param string name of the cookie. - * @param string - * @param string - * @param bool - * @return void - * @throws Nette\InvalidStateException if HTTP headers have been sent - */ - public function deleteCookie($name, $path = NULL, $domain = NULL, $secure = NULL) - { - if (headers_sent($file, $line)) { - throw new Nette\InvalidStateException("Cannot delete cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); - } - - setcookie( - $name, - FALSE, - 254400000, - $path === NULL ? $this->cookiePath : (string) $path, - $domain === NULL ? $this->cookieDomain : (string) $domain, - $secure === NULL ? $this->cookieSecure : (bool) $secure, - TRUE // doesn't matter with delete - ); - } - -} +1, 201=>1, 202=>1, 203=>1, 204=>1, 205=>1, 206=>1, + 300=>1, 301=>1, 302=>1, 303=>1, 304=>1, 307=>1, + 400=>1, 401=>1, 403=>1, 404=>1, 405=>1, 406=>1, 408=>1, 410=>1, 412=>1, 415=>1, 416=>1, + 500=>1, 501=>1, 503=>1, 505=>1 + ); + + if (!isset($allowed[$code])) { + throw new Nette\InvalidArgumentException("Bad HTTP response '$code'."); + + } elseif (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot set HTTP code after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + + } else { + $this->code = $code; + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + header($protocol . ' ' . $code, TRUE, $code); + } + return $this; + } + + + + /** + * Returns HTTP response code. + * @return int + */ + public function getCode() + { + return $this->code; + } + + + + /** + * Sends a HTTP header and replaces a previous one. + * @param string header name + * @param string header value + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setHeader($name, $value) + { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + + if ($value === NULL && function_exists('header_remove')) { + header_remove($name); + } elseif (strcasecmp($name, 'Content-Length') === 0 && ini_get('zlib.output_compression')) { + // ignore, PHP bug #44164 + } else { + header($name . ': ' . $value, TRUE, $this->code); + } + return $this; + } + + + + /** + * Adds HTTP header. + * @param string header name + * @param string header value + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function addHeader($name, $value) + { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + + header($name . ': ' . $value, FALSE, $this->code); + return $this; + } + + + + /** + * Sends a Content-type HTTP header. + * @param string mime-type + * @param string charset + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setContentType($type, $charset = NULL) + { + $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); + return $this; + } + + + + /** + * Redirects to a new URL. Note: call exit() after it. + * @param string URL + * @param int HTTP code + * @return void + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function redirect($url, $code = self::S302_FOUND) + { + if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('#^Microsoft-IIS/[1-5]#', $_SERVER['SERVER_SOFTWARE']) + && $this->getHeader('Set-Cookie') !== NULL + ) { + $this->setHeader('Refresh', "0;url=$url"); + return; + } + + $this->setCode($code); + $this->setHeader('Location', $url); + echo "

    Redirect

    \n\n

    Please click here to continue.

    "; + } + + + + /** + * Sets the number of seconds before a page cached on a browser expires. + * @param string|int|DateTime time, value 0 means "until the browser is closed" + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setExpiration($time) + { + if (!$time) { // no cache + $this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate'); + $this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT'); + return $this; + } + + $time = Nette\DateTime::from($time); + $this->setHeader('Cache-Control', 'max-age=' . ($time->format('U') - time())); + $this->setHeader('Expires', self::date($time)); + return $this; + } + + + + /** + * Checks if headers have been sent. + * @return bool + */ + public function isSent() + { + return headers_sent(); + } + + + + /** + * Return the value of the HTTP header. + * @param string + * @param mixed + * @return mixed + */ + public function getHeader($header, $default = NULL) + { + $header .= ':'; + $len = strlen($header); + foreach (headers_list() as $item) { + if (strncasecmp($item, $header, $len) === 0) { + return ltrim(substr($item, $len)); + } + } + return $default; + } + + + + /** + * Returns a list of headers to sent. + * @return array + */ + public function getHeaders() + { + $headers = array(); + foreach (headers_list() as $header) { + $a = strpos($header, ':'); + $headers[substr($header, 0, $a)] = (string) substr($header, $a + 2); + } + return $headers; + } + + + + /** + * Returns HTTP valid date format. + * @param string|int|DateTime + * @return string + */ + public static function date($time = NULL) + { + $time = Nette\DateTime::from($time); + $time->setTimezone(new \DateTimeZone('GMT')); + return $time->format('D, d M Y H:i:s \G\M\T'); + } + + + + /** + * @return void + */ + public function __destruct() + { + if (self::$fixIE && isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE + && in_array($this->code, array(400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505), TRUE) + && $this->getHeader('Content-Type', 'text/html') === 'text/html' + ) { + echo Nette\Utils\Strings::random(2e3, " \t\r\n"); // sends invisible garbage for IE + self::$fixIE = FALSE; + } + } + + + + /** + * Sends a cookie. + * @param string name of the cookie + * @param string value + * @param string|int|DateTime expiration time, value 0 means "until the browser is closed" + * @param string + * @param string + * @param bool + * @param bool + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setCookie($name, $value, $time, $path = NULL, $domain = NULL, $secure = NULL, $httpOnly = NULL) + { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot set cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + + setcookie( + $name, + $value, + $time ? Nette\DateTime::from($time)->format('U') : 0, + $path === NULL ? $this->cookiePath : (string) $path, + $domain === NULL ? $this->cookieDomain : (string) $domain, + $secure === NULL ? $this->cookieSecure : (bool) $secure, + $httpOnly === NULL ? $this->cookieHttpOnly : (bool) $httpOnly + ); + + if (ini_get('suhosin.cookie.encrypt')) { + return $this; + } + + $flatten = array(); + foreach (headers_list() as $header) { + if (preg_match('#^Set-Cookie: .+?=#', $header, $m)) { + $flatten[$m[0]] = $header; + if (PHP_VERSION_ID < 50300) { // multiple deleting due PHP bug #61605 + header('Set-Cookie:'); + } else { + header_remove('Set-Cookie'); + } + } + } + foreach (array_values($flatten) as $key => $header) { + header($header, $key === 0); + } + + return $this; + } + + + + /** + * Deletes a cookie. + * @param string name of the cookie. + * @param string + * @param string + * @param bool + * @return void + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function deleteCookie($name, $path = NULL, $domain = NULL, $secure = NULL) + { + $this->setCookie($name, FALSE, 0, $path, $domain, $secure); + } + +} diff --git a/libs/Nette/Http/Session.php b/libs/Nette/Http/Session.php index c94a188..c7d17b3 100644 --- a/libs/Nette/Http/Session.php +++ b/libs/Nette/Http/Session.php @@ -1,553 +1,591 @@ - '', // must be disabled because PHP implementation is invalid - 'use_cookies' => 1, // must be enabled to prevent Session Hijacking and Fixation - 'use_only_cookies' => 1, // must be enabled to prevent Session Fixation - 'use_trans_sid' => 0, // must be disabled to prevent Session Hijacking and Fixation - - // cookies - 'cookie_lifetime' => 0, // until the browser is closed - 'cookie_path' => '/', // cookie is available within the entire domain - 'cookie_domain' => '', // cookie is available on current subdomain only - 'cookie_secure' => FALSE, // cookie is available on HTTP & HTTPS - 'cookie_httponly' => TRUE,// must be enabled to prevent Session Hijacking - - // other - 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME,// 3 hours - 'cache_limiter' => NULL, // (default "nocache", special value "\0") - 'cache_expire' => NULL, // (default "180") - 'hash_function' => NULL, // (default "0", means MD5) - 'hash_bits_per_character' => NULL, // (default "4") - ); - - /** @var IRequest */ - private $request; - - /** @var IResponse */ - private $response; - - - - public function __construct(IRequest $request, IResponse $response) - { - $this->request = $request; - $this->response = $response; - } - - - - /** - * Starts and initializes session data. - * @throws Nette\InvalidStateException - * @return void - */ - public function start() - { - if (self::$started) { - return; - - } elseif (self::$started === NULL && defined('SID')) { - throw new Nette\InvalidStateException('A session had already been started by session.auto-start or session_start().'); - } - - $this->configure($this->options); - - Nette\Diagnostics\Debugger::tryError(); - session_start(); - if (Nette\Diagnostics\Debugger::catchError($e)) { - @session_write_close(); // this is needed - throw new Nette\InvalidStateException('session_start(): ' . $e->getMessage(), 0, $e); - } - - self::$started = TRUE; - if ($this->regenerationNeeded) { - session_regenerate_id(TRUE); - $this->regenerationNeeded = FALSE; - } - - /* structure: - __NF: Counter, BrowserKey, Data, Meta - DATA: namespace->variable = data - META: namespace->variable = Timestamp, Browser, Version - */ - - unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']); // old unused structures - - // initialize structures - $nf = & $_SESSION['__NF']; - if (empty($nf)) { // new session - $nf = array('C' => 0); - } else { - $nf['C']++; - } - - // browser closing detection - $browserKey = $this->request->getCookie('nette-browser'); - if (!$browserKey) { - $browserKey = Nette\Utils\Strings::random(); - } - $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey; - $nf['B'] = $browserKey; - - // resend cookie - $this->sendCookie(); - - // process meta metadata - if (isset($nf['META'])) { - $now = time(); - // expire namespace variables - foreach ($nf['META'] as $namespace => $metadata) { - if (is_array($metadata)) { - foreach ($metadata as $variable => $value) { - if ((!empty($value['B']) && $browserClosed) || (!empty($value['T']) && $now > $value['T']) // whenBrowserIsClosed || Time - || ($variable !== '' && is_object($nf['DATA'][$namespace][$variable]) && (isset($value['V']) ? $value['V'] : NULL) // Version - !== Nette\Reflection\ClassType::from($nf['DATA'][$namespace][$variable])->getAnnotation('serializationVersion')) - ) { - if ($variable === '') { // expire whole namespace - unset($nf['META'][$namespace], $nf['DATA'][$namespace]); - continue 2; - } - unset($nf['META'][$namespace][$variable], $nf['DATA'][$namespace][$variable]); - } - } - } - } - } - - register_shutdown_function(array($this, 'clean')); - } - - - - /** - * Has been session started? - * @return bool - */ - public function isStarted() - { - return (bool) self::$started; - } - - - - /** - * Ends the current session and store session data. - * @return void - */ - public function close() - { - if (self::$started) { - $this->clean(); - session_write_close(); - self::$started = FALSE; - } - } - - - - /** - * Destroys all data registered to a session. - * @return void - */ - public function destroy() - { - if (!self::$started) { - throw new Nette\InvalidStateException('Session is not started.'); - } - - session_destroy(); - $_SESSION = NULL; - self::$started = FALSE; - if (!$this->response->isSent()) { - $params = session_get_cookie_params(); - $this->response->deleteCookie(session_name(), $params['path'], $params['domain'], $params['secure']); - } - } - - - - /** - * Does session exists for the current request? - * @return bool - */ - public function exists() - { - return self::$started || $this->request->getCookie(session_name()) !== NULL; - } - - - - /** - * Regenerates the session ID. - * @throws Nette\InvalidStateException - * @return void - */ - public function regenerateId() - { - if (self::$started) { - if (headers_sent($file, $line)) { - throw new Nette\InvalidStateException("Cannot regenerate session ID after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); - } - session_regenerate_id(TRUE); - - } else { - $this->regenerationNeeded = TRUE; - } - } - - - - /** - * Returns the current session ID. Don't make dependencies, can be changed for each request. - * @return string - */ - public function getId() - { - return session_id(); - } - - - - /** - * Sets the session name to a specified one. - * @param string - * @return Session provides a fluent interface - */ - public function setName($name) - { - if (!is_string($name) || !preg_match('#[^0-9.][^.]*$#A', $name)) { - throw new Nette\InvalidArgumentException('Session name must be a string and cannot contain dot.'); - } - - session_name($name); - return $this->setOptions(array( - 'name' => $name, - )); - } - - - - /** - * Gets the session name. - * @return string - */ - public function getName() - { - return session_name(); - } - - - - /********************* namespaces management ****************d*g**/ - - - - /** - * Returns specified session namespace. - * @param string - * @param string - * @return SessionNamespace - * @throws Nette\InvalidArgumentException - */ - public function getNamespace($name, $class = 'Nette\Http\SessionNamespace') - { - return new $class($this, $name); - } - - - - /** - * Checks if a session namespace exist and is not empty. - * @param string - * @return bool - */ - public function hasNamespace($name) - { - if ($this->exists() && !self::$started) { - $this->start(); - } - - return !empty($_SESSION['__NF']['DATA'][$name]); - } - - - - /** - * Iteration over all namespaces. - * @return \ArrayIterator - */ - public function getIterator() - { - if ($this->exists() && !self::$started) { - $this->start(); - } - - if (isset($_SESSION['__NF']['DATA'])) { - return new \ArrayIterator(array_keys($_SESSION['__NF']['DATA'])); - - } else { - return new \ArrayIterator; - } - } - - - - /** - * Cleans and minimizes meta structures. - * @return void - */ - public function clean() - { - if (!self::$started || empty($_SESSION)) { - return; - } - - $nf = & $_SESSION['__NF']; - if (isset($nf['META']) && is_array($nf['META'])) { - foreach ($nf['META'] as $name => $foo) { - if (empty($nf['META'][$name])) { - unset($nf['META'][$name]); - } - } - } - - if (empty($nf['META'])) { - unset($nf['META']); - } - - if (empty($nf['DATA'])) { - unset($nf['DATA']); - } - - if (empty($_SESSION)) { - //$this->destroy(); only when shutting down - } - } - - - - /********************* configuration ****************d*g**/ - - - - /** - * Sets session options. - * @param array - * @return Session provides a fluent interface - * @throws Nette\NotSupportedException - * @throws Nette\InvalidStateException - */ - public function setOptions(array $options) - { - if (self::$started) { - $this->configure($options); - } - $this->options = $options + $this->options; - return $this; - } - - - - /** - * Returns all session options. - * @return array - */ - public function getOptions() - { - return $this->options; - } - - - - /** - * Configurates session environment. - * @param array - * @return void - */ - private function configure(array $config) - { - $special = array('cache_expire' => 1, 'cache_limiter' => 1, 'save_path' => 1, 'name' => 1); - - foreach ($config as $key => $value) { - if (!strncmp($key, 'session.', 8)) { // back compatibility - $key = substr($key, 8); - } - - if ($value === NULL) { - continue; - - } elseif (isset($special[$key])) { - if (self::$started) { - throw new Nette\InvalidStateException("Unable to set '$key' when session has been started."); - } - $key = "session_$key"; - $key($value); - - } elseif (strncmp($key, 'cookie_', 7) === 0) { - if (!isset($cookie)) { - $cookie = session_get_cookie_params(); - } - $cookie[substr($key, 7)] = $value; - - } elseif (!function_exists('ini_set')) { - if (ini_get($key) != $value && !Nette\Framework::$iAmUsingBadHost) { // intentionally == - throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); - } - - } else { - if (self::$started) { - throw new Nette\InvalidStateException("Unable to set '$key' when session has been started."); - } - ini_set("session.$key", $value); - } - } - - if (isset($cookie)) { - session_set_cookie_params( - $cookie['lifetime'], $cookie['path'], $cookie['domain'], - $cookie['secure'], $cookie['httponly'] - ); - if (self::$started) { - $this->sendCookie(); - } - } - } - - - - /** - * Sets the amount of time allowed between requests before the session will be terminated. - * @param string|int|DateTime time, value 0 means "until the browser is closed" - * @return Session provides a fluent interface - */ - public function setExpiration($time) - { - if (empty($time)) { - return $this->setOptions(array( - 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME, - 'cookie_lifetime' => 0, - )); - - } else { - $time = Nette\DateTime::from($time)->format('U') - time(); - return $this->setOptions(array( - 'gc_maxlifetime' => $time, - 'cookie_lifetime' => $time, - )); - } - } - - - - /** - * Sets the session cookie parameters. - * @param string path - * @param string domain - * @param bool secure - * @return Session provides a fluent interface - */ - public function setCookieParams($path, $domain = NULL, $secure = NULL) - { - return $this->setOptions(array( - 'cookie_path' => $path, - 'cookie_domain' => $domain, - 'cookie_secure' => $secure - )); - } - - - - /** - * Returns the session cookie parameters. - * @return array containing items: lifetime, path, domain, secure, httponly - */ - public function getCookieParams() - { - return session_get_cookie_params(); - } - - - - /** - * Sets path of the directory used to save session data. - * @return Session provides a fluent interface - */ - public function setSavePath($path) - { - return $this->setOptions(array( - 'save_path' => $path, - )); - } - - - - /** - * Sets user session storage. - * @return Session provides a fluent interface - */ - public function setStorage(ISessionStorage $storage) - { - if (self::$started) { - throw new Nette\InvalidStateException("Unable to set storage when session has been started."); - } - session_set_save_handler( - array($storage, 'open'), array($storage, 'close'), array($storage, 'read'), - array($storage, 'write'), array($storage, 'remove'), array($storage, 'clean') - ); - } - - - - /** - * Sends the session cookies. - * @return void - */ - private function sendCookie() - { - $cookie = $this->getCookieParams(); - $this->response->setCookie( - session_name(), session_id(), - $cookie['lifetime'] ? $cookie['lifetime'] + time() : 0, - $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly'] - - )->setCookie( - 'nette-browser', $_SESSION['__NF']['B'], - Response::BROWSER, $cookie['path'], $cookie['domain'] - ); - } - -} + '', // must be disabled because PHP implementation is invalid + 'use_cookies' => 1, // must be enabled to prevent Session Hijacking and Fixation + 'use_only_cookies' => 1, // must be enabled to prevent Session Fixation + 'use_trans_sid' => 0, // must be disabled to prevent Session Hijacking and Fixation + + // cookies + 'cookie_lifetime' => 0, // until the browser is closed + 'cookie_path' => '/', // cookie is available within the entire domain + 'cookie_domain' => '', // cookie is available on current subdomain only + 'cookie_secure' => FALSE, // cookie is available on HTTP & HTTPS + 'cookie_httponly' => TRUE,// must be enabled to prevent Session Hijacking + + // other + 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME,// 3 hours + 'cache_limiter' => NULL, // (default "nocache", special value "\0") + 'cache_expire' => NULL, // (default "180") + 'hash_function' => NULL, // (default "0", means MD5) + 'hash_bits_per_character' => NULL, // (default "4") + ); + + /** @var IRequest */ + private $request; + + /** @var IResponse */ + private $response; + + + + public function __construct(IRequest $request, IResponse $response) + { + $this->request = $request; + $this->response = $response; + } + + + + /** + * Starts and initializes session data. + * @throws Nette\InvalidStateException + * @return void + */ + public function start() + { + if (self::$started) { + return; + } + + $this->configure($this->options); + + Nette\Diagnostics\Debugger::tryError(); + session_start(); + if (Nette\Diagnostics\Debugger::catchError($e) && !session_id()) { + @session_write_close(); // this is needed + throw new Nette\InvalidStateException('session_start(): ' . $e->getMessage(), 0, $e); + } + + self::$started = TRUE; + + /* structure: + __NF: Counter, BrowserKey, Data, Meta, Time + DATA: section->variable = data + META: section->variable = Timestamp, Browser, Version + */ + + unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']); // old unused structures + + // initialize structures + $nf = & $_SESSION['__NF']; + if (empty($nf)) { // new session + $nf = array('C' => 0); + } else { + $nf['C']++; + } + + // session regenerate every 30 minutes + $nfTime = & $nf['Time']; + $time = time(); + if ($time - $nfTime > self::REGENERATE_INTERVAL) { + $this->regenerated = $this->regenerated || isset($nfTime); + $nfTime = $time; + } + + // browser closing detection + $browserKey = $this->request->getCookie('nette-browser'); + if (!$browserKey) { + $browserKey = Nette\Utils\Strings::random(); + } + $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey; + $nf['B'] = $browserKey; + + // resend cookie + $this->sendCookie(); + + // process meta metadata + if (isset($nf['META'])) { + $now = time(); + // expire section variables + foreach ($nf['META'] as $section => $metadata) { + if (is_array($metadata)) { + foreach ($metadata as $variable => $value) { + if ((!empty($value['B']) && $browserClosed) || (!empty($value['T']) && $now > $value['T']) // whenBrowserIsClosed || Time + || (isset($nf['DATA'][$section][$variable]) && is_object($nf['DATA'][$section][$variable]) && (isset($value['V']) ? $value['V'] : NULL) // Version + != Nette\Reflection\ClassType::from($nf['DATA'][$section][$variable])->getAnnotation('serializationVersion')) // intentionally != + ) { + if ($variable === '') { // expire whole section + unset($nf['META'][$section], $nf['DATA'][$section]); + continue 2; + } + unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]); + } + } + } + } + } + + if ($this->regenerated) { + $this->regenerated = FALSE; + $this->regenerateId(); + } + + register_shutdown_function(array($this, 'clean')); + } + + + + /** + * Has been session started? + * @return bool + */ + public function isStarted() + { + return (bool) self::$started; + } + + + + /** + * Ends the current session and store session data. + * @return void + */ + public function close() + { + if (self::$started) { + $this->clean(); + session_write_close(); + self::$started = FALSE; + } + } + + + + /** + * Destroys all data registered to a session. + * @return void + */ + public function destroy() + { + if (!self::$started) { + throw new Nette\InvalidStateException('Session is not started.'); + } + + session_destroy(); + $_SESSION = NULL; + self::$started = FALSE; + if (!$this->response->isSent()) { + $params = session_get_cookie_params(); + $this->response->deleteCookie(session_name(), $params['path'], $params['domain'], $params['secure']); + } + } + + + + /** + * Does session exists for the current request? + * @return bool + */ + public function exists() + { + return self::$started || $this->request->getCookie($this->getName()) !== NULL; + } + + + + /** + * Regenerates the session ID. + * @throws Nette\InvalidStateException + * @return void + */ + public function regenerateId() + { + if (self::$started && !$this->regenerated) { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot regenerate session ID after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + session_regenerate_id(TRUE); + session_write_close(); + $backup = $_SESSION; + session_start(); + $_SESSION = $backup; + } + $this->regenerated = TRUE; + } + + + + /** + * Returns the current session ID. Don't make dependencies, can be changed for each request. + * @return string + */ + public function getId() + { + return session_id(); + } + + + + /** + * Sets the session name to a specified one. + * @param string + * @return Session provides a fluent interface + */ + public function setName($name) + { + if (!is_string($name) || !preg_match('#[^0-9.][^.]*$#A', $name)) { + throw new Nette\InvalidArgumentException('Session name must be a string and cannot contain dot.'); + } + + session_name($name); + return $this->setOptions(array( + 'name' => $name, + )); + } + + + + /** + * Gets the session name. + * @return string + */ + public function getName() + { + return isset($this->options['name']) ? $this->options['name'] : session_name(); + } + + + + /********************* sections management ****************d*g**/ + + + + /** + * Returns specified session section. + * @param string + * @param string + * @return SessionSection + * @throws Nette\InvalidArgumentException + */ + public function getSection($section, $class = 'Nette\Http\SessionSection') + { + return new $class($this, $section); + } + + + + /** @deprecated */ + function getNamespace($section) + { + trigger_error(__METHOD__ . '() is deprecated; use getSection() instead.', E_USER_WARNING); + return $this->getSection($section); + } + + + + /** + * Checks if a session section exist and is not empty. + * @param string + * @return bool + */ + public function hasSection($section) + { + if ($this->exists() && !self::$started) { + $this->start(); + } + + return !empty($_SESSION['__NF']['DATA'][$section]); + } + + + + /** + * Iteration over all sections. + * @return \ArrayIterator + */ + public function getIterator() + { + if ($this->exists() && !self::$started) { + $this->start(); + } + + if (isset($_SESSION['__NF']['DATA'])) { + return new \ArrayIterator(array_keys($_SESSION['__NF']['DATA'])); + + } else { + return new \ArrayIterator; + } + } + + + + /** + * Cleans and minimizes meta structures. + * @return void + */ + public function clean() + { + if (!self::$started || empty($_SESSION)) { + return; + } + + $nf = & $_SESSION['__NF']; + if (isset($nf['META']) && is_array($nf['META'])) { + foreach ($nf['META'] as $name => $foo) { + if (empty($nf['META'][$name])) { + unset($nf['META'][$name]); + } + } + } + + if (empty($nf['META'])) { + unset($nf['META']); + } + + if (empty($nf['DATA'])) { + unset($nf['DATA']); + } + + if (empty($_SESSION)) { + //$this->destroy(); only when shutting down + } + } + + + + /********************* configuration ****************d*g**/ + + + + /** + * Sets session options. + * @param array + * @return Session provides a fluent interface + * @throws Nette\NotSupportedException + * @throws Nette\InvalidStateException + */ + public function setOptions(array $options) + { + if (self::$started) { + $this->configure($options); + } + $this->options = $options + $this->options; + if (!empty($options['auto_start'])) { + $this->start(); + } + return $this; + } + + + + /** + * Returns all session options. + * @return array + */ + public function getOptions() + { + return $this->options; + } + + + + /** + * Configurates session environment. + * @param array + * @return void + */ + private function configure(array $config) + { + $special = array('cache_expire' => 1, 'cache_limiter' => 1, 'save_path' => 1, 'name' => 1); + + foreach ($config as $key => $value) { + if (!strncmp($key, 'session.', 8)) { // back compatibility + $key = substr($key, 8); + } + $key = strtolower(preg_replace('#(.)(?=[A-Z])#', '$1_', $key)); + + if ($value === NULL || ini_get("session.$key") == $value) { // intentionally == + continue; + + } elseif (strncmp($key, 'cookie_', 7) === 0) { + if (!isset($cookie)) { + $cookie = session_get_cookie_params(); + } + $cookie[substr($key, 7)] = $value; + + } else { + if (defined('SID')) { + throw new Nette\InvalidStateException("Unable to set 'session.$key' to value '$value' when session has been started" . ($this->started ? "." : " by session.auto_start or session_start().")); + } + if (isset($special[$key])) { + $key = "session_$key"; + $key($value); + + } elseif (function_exists('ini_set')) { + ini_set("session.$key", $value); + + } elseif (!Nette\Framework::$iAmUsingBadHost) { + throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); + } + } + } + + if (isset($cookie)) { + session_set_cookie_params( + $cookie['lifetime'], $cookie['path'], $cookie['domain'], + $cookie['secure'], $cookie['httponly'] + ); + if (self::$started) { + $this->sendCookie(); + } + } + } + + + + /** + * Sets the amount of time allowed between requests before the session will be terminated. + * @param string|int|DateTime time, value 0 means "until the browser is closed" + * @return Session provides a fluent interface + */ + public function setExpiration($time) + { + if (empty($time)) { + return $this->setOptions(array( + 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME, + 'cookie_lifetime' => 0, + )); + + } else { + $time = Nette\DateTime::from($time)->format('U') - time(); + return $this->setOptions(array( + 'gc_maxlifetime' => $time, + 'cookie_lifetime' => $time, + )); + } + } + + + + /** + * Sets the session cookie parameters. + * @param string path + * @param string domain + * @param bool secure + * @return Session provides a fluent interface + */ + public function setCookieParameters($path, $domain = NULL, $secure = NULL) + { + return $this->setOptions(array( + 'cookie_path' => $path, + 'cookie_domain' => $domain, + 'cookie_secure' => $secure + )); + } + + + + /** + * Returns the session cookie parameters. + * @return array containing items: lifetime, path, domain, secure, httponly + */ + public function getCookieParameters() + { + return session_get_cookie_params(); + } + + + + /** @deprecated */ + function setCookieParams($path, $domain = NULL, $secure = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use setCookieParameters() instead.', E_USER_WARNING); + return $this->setCookieParameters($path, $domain, $secure); + } + + + + /** + * Sets path of the directory used to save session data. + * @return Session provides a fluent interface + */ + public function setSavePath($path) + { + return $this->setOptions(array( + 'save_path' => $path, + )); + } + + + + /** + * Sets user session storage. + * @return Session provides a fluent interface + */ + public function setStorage(ISessionStorage $storage) + { + if (self::$started) { + throw new Nette\InvalidStateException("Unable to set storage when session has been started."); + } + session_set_save_handler( + array($storage, 'open'), array($storage, 'close'), array($storage, 'read'), + array($storage, 'write'), array($storage, 'remove'), array($storage, 'clean') + ); + } + + + + /** + * Sends the session cookies. + * @return void + */ + private function sendCookie() + { + $cookie = $this->getCookieParameters(); + $this->response->setCookie( + session_name(), session_id(), + $cookie['lifetime'] ? $cookie['lifetime'] + time() : 0, + $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly'] + + )->setCookie( + 'nette-browser', $_SESSION['__NF']['B'], + Response::BROWSER, $cookie['path'], $cookie['domain'] + ); + } + +} diff --git a/libs/Nette/Http/SessionNamespace.php b/libs/Nette/Http/SessionSection.php similarity index 77% rename from libs/Nette/Http/SessionNamespace.php rename to libs/Nette/Http/SessionSection.php index 60a7506..5fa0e47 100644 --- a/libs/Nette/Http/SessionNamespace.php +++ b/libs/Nette/Http/SessionSection.php @@ -1,272 +1,273 @@ -session = $session; - $this->name = $name; - } - - - - /** - * Do not call directly. Use Session::getNamespace(). - */ - private function start() - { - if ($this->meta === FALSE) { - $this->session->start(); - $this->data = & $_SESSION['__NF']['DATA'][$this->name]; - $this->meta = & $_SESSION['__NF']['META'][$this->name]; - } - } - - - - /** - * Returns an iterator over all namespace variables. - * @return \ArrayIterator - */ - public function getIterator() - { - $this->start(); - if (isset($this->data)) { - return new \ArrayIterator($this->data); - } else { - return new \ArrayIterator; - } - } - - - - /** - * Sets a variable in this session namespace. - * @param string name - * @param mixed value - * @return void - */ - public function __set($name, $value) - { - $this->start(); - $this->data[$name] = $value; - if (is_object($value)) { - $this->meta[$name]['V'] = Nette\Reflection\ClassType::from($value)->getAnnotation('serializationVersion'); - } - } - - - - /** - * Gets a variable from this session namespace. - * @param string name - * @return mixed - */ - public function &__get($name) - { - $this->start(); - if ($this->warnOnUndefined && !array_key_exists($name, $this->data)) { - trigger_error("The variable '$name' does not exist in session namespace", E_USER_NOTICE); - } - - return $this->data[$name]; - } - - - - /** - * Determines whether a variable in this session namespace is set. - * @param string name - * @return bool - */ - public function __isset($name) - { - if ($this->session->exists()) { - $this->start(); - } - return isset($this->data[$name]); - } - - - - /** - * Unsets a variable in this session namespace. - * @param string name - * @return void - */ - public function __unset($name) - { - $this->start(); - unset($this->data[$name], $this->meta[$name]); - } - - - - /** - * Sets a variable in this session namespace. - * @param string name - * @param mixed value - * @return void - */ - public function offsetSet($name, $value) - { - $this->__set($name, $value); - } - - - - /** - * Gets a variable from this session namespace. - * @param string name - * @return mixed - */ - public function offsetGet($name) - { - return $this->__get($name); - } - - - - /** - * Determines whether a variable in this session namespace is set. - * @param string name - * @return bool - */ - public function offsetExists($name) - { - return $this->__isset($name); - } - - - - /** - * Unsets a variable in this session namespace. - * @param string name - * @return void - */ - public function offsetUnset($name) - { - $this->__unset($name); - } - - - - /** - * Sets the expiration of the namespace or specific variables. - * @param string|int|DateTime time, value 0 means "until the browser is closed" - * @param mixed optional list of variables / single variable to expire - * @return SessionNamespace provides a fluent interface - */ - public function setExpiration($time, $variables = NULL) - { - $this->start(); - if (empty($time)) { - $time = NULL; - $whenBrowserIsClosed = TRUE; - } else { - $time = Nette\DateTime::from($time)->format('U'); - $max = ini_get('session.gc_maxlifetime'); - if ($time - time() > $max + 3) { // bulgarian constant - trigger_error("The expiration time is greater than the session expiration $max seconds", E_USER_NOTICE); - } - $whenBrowserIsClosed = FALSE; - } - - if ($variables === NULL) { // to entire namespace - $this->meta['']['T'] = $time; - $this->meta['']['B'] = $whenBrowserIsClosed; - - } elseif (is_array($variables)) { // to variables - foreach ($variables as $variable) { - $this->meta[$variable]['T'] = $time; - $this->meta[$variable]['B'] = $whenBrowserIsClosed; - } - - } else { // to variable - $this->meta[$variables]['T'] = $time; - $this->meta[$variables]['B'] = $whenBrowserIsClosed; - } - return $this; - } - - - - /** - * Removes the expiration from the namespace or specific variables. - * @param mixed optional list of variables / single variable to expire - * @return void - */ - public function removeExpiration($variables = NULL) - { - $this->start(); - if ($variables === NULL) { - // from entire namespace - unset($this->meta['']['T'], $this->meta['']['B']); - - } elseif (is_array($variables)) { - // from variables - foreach ($variables as $variable) { - unset($this->meta[$variable]['T'], $this->meta[$variable]['B']); - } - } else { - unset($this->meta[$variables]['T'], $this->meta[$variable]['B']); - } - } - - - - /** - * Cancels the current session namespace. - * @return void - */ - public function remove() - { - $this->data = NULL; - $this->meta = NULL; - } - -} +session = $session; + $this->name = $name; + } + + + + /** + * Do not call directly. Use Session::getNamespace(). + */ + private function start() + { + if ($this->meta === FALSE) { + $this->session->start(); + $this->data = & $_SESSION['__NF']['DATA'][$this->name]; + $this->meta = & $_SESSION['__NF']['META'][$this->name]; + } + } + + + + /** + * Returns an iterator over all section variables. + * @return \ArrayIterator + */ + public function getIterator() + { + $this->start(); + if (isset($this->data)) { + return new \ArrayIterator($this->data); + } else { + return new \ArrayIterator; + } + } + + + + /** + * Sets a variable in this session section. + * @param string name + * @param mixed value + * @return void + */ + public function __set($name, $value) + { + $this->start(); + $this->data[$name] = $value; + if (is_object($value)) { + $this->meta[$name]['V'] = Nette\Reflection\ClassType::from($value)->getAnnotation('serializationVersion'); + } + } + + + + /** + * Gets a variable from this session section. + * @param string name + * @return mixed + */ + public function &__get($name) + { + $this->start(); + if ($this->warnOnUndefined && !array_key_exists($name, $this->data)) { + trigger_error("The variable '$name' does not exist in session section", E_USER_NOTICE); + } + + return $this->data[$name]; + } + + + + /** + * Determines whether a variable in this session section is set. + * @param string name + * @return bool + */ + public function __isset($name) + { + if ($this->session->exists()) { + $this->start(); + } + return isset($this->data[$name]); + } + + + + /** + * Unsets a variable in this session section. + * @param string name + * @return void + */ + public function __unset($name) + { + $this->start(); + unset($this->data[$name], $this->meta[$name]); + } + + + + /** + * Sets a variable in this session section. + * @param string name + * @param mixed value + * @return void + */ + public function offsetSet($name, $value) + { + $this->__set($name, $value); + } + + + + /** + * Gets a variable from this session section. + * @param string name + * @return mixed + */ + public function offsetGet($name) + { + return $this->__get($name); + } + + + + /** + * Determines whether a variable in this session section is set. + * @param string name + * @return bool + */ + public function offsetExists($name) + { + return $this->__isset($name); + } + + + + /** + * Unsets a variable in this session section. + * @param string name + * @return void + */ + public function offsetUnset($name) + { + $this->__unset($name); + } + + + + /** + * Sets the expiration of the section or specific variables. + * @param string|int|DateTime time, value 0 means "until the browser is closed" + * @param mixed optional list of variables / single variable to expire + * @return SessionSection provides a fluent interface + */ + public function setExpiration($time, $variables = NULL) + { + $this->start(); + if (empty($time)) { + $time = NULL; + $whenBrowserIsClosed = TRUE; + } else { + $time = Nette\DateTime::from($time)->format('U'); + $max = ini_get('session.gc_maxlifetime'); + if ($time - time() > $max + 3) { // bulgarian constant + trigger_error("The expiration time is greater than the session expiration $max seconds", E_USER_NOTICE); + } + $whenBrowserIsClosed = FALSE; + } + + if ($variables === NULL) { // to entire section + $this->meta['']['T'] = $time; + $this->meta['']['B'] = $whenBrowserIsClosed; + + } elseif (is_array($variables)) { // to variables + foreach ($variables as $variable) { + $this->meta[$variable]['T'] = $time; + $this->meta[$variable]['B'] = $whenBrowserIsClosed; + } + + } else { // to variable + $this->meta[$variables]['T'] = $time; + $this->meta[$variables]['B'] = $whenBrowserIsClosed; + } + return $this; + } + + + + /** + * Removes the expiration from the section or specific variables. + * @param mixed optional list of variables / single variable to expire + * @return void + */ + public function removeExpiration($variables = NULL) + { + $this->start(); + if ($variables === NULL) { + // from entire section + unset($this->meta['']['T'], $this->meta['']['B']); + + } elseif (is_array($variables)) { + // from variables + foreach ($variables as $variable) { + unset($this->meta[$variable]['T'], $this->meta[$variable]['B']); + } + } else { + unset($this->meta[$variables]['T'], $this->meta[$variable]['B']); + } + } + + + + /** + * Cancels the current session section. + * @return void + */ + public function remove() + { + $this->start(); + $this->data = NULL; + $this->meta = NULL; + } + +} diff --git a/libs/Nette/Http/Url.php b/libs/Nette/Http/Url.php index 8001b48..381fcd3 100644 --- a/libs/Nette/Http/Url.php +++ b/libs/Nette/Http/Url.php @@ -1,526 +1,526 @@ - - * scheme user password host port basePath relativeUrl - * | | | | | | | - * /--\ /--\ /------\ /-------\ /--\/--\/----------------------------\ - * http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl - * \__________________________/\____________/^\________/^\______/ - * | | | | - * authority path query fragment - * - * - * - authority: [user[:password]@]host[:port] - * - hostUrl: http://user:password@nette.org:8042 - * - basePath: /en/ (everything before relative URI not including the script name) - * - baseUrl: http://user:password@nette.org:8042/en/ - * - relativeUrl: manual.php - * - * @author David Grudl - * - * @property string $scheme - * @property string $user - * @property string $password - * @property string $host - * @property string $port - * @property string $path - * @property string $query - * @property string $fragment - * @property-read string $absoluteUrl - * @property-read string $authority - * @property-read string $hostUrl - * @property-read string $basePath - * @property-read string $baseUrl - * @property-read string $relativeUrl - */ -class Url extends Nette\FreezableObject -{ - /** @var array */ - public static $defaultPorts = array( - 'http' => 80, - 'https' => 443, - 'ftp' => 21, - 'news' => 119, - 'nntp' => 119, - ); - - /** @var string */ - private $scheme = ''; - - /** @var string */ - private $user = ''; - - /** @var string */ - private $pass = ''; - - /** @var string */ - private $host = ''; - - /** @var int */ - private $port = NULL; - - /** @var string */ - private $path = ''; - - /** @var string */ - private $query = ''; - - /** @var string */ - private $fragment = ''; - - - - /** - * @param string URL - * @throws Nette\WebNette\InvalidArgumentException - */ - public function __construct($url = NULL) - { - if (is_string($url)) { - $parts = @parse_url($url); // @ - is escalated to exception - if ($parts === FALSE) { - throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'."); - } - - foreach ($parts as $key => $val) { - $this->$key = $val; - } - - if (!$this->port && isset(self::$defaultPorts[$this->scheme])) { - $this->port = self::$defaultPorts[$this->scheme]; - } - - if ($this->path === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { - $this->path = '/'; - } - - } elseif ($url instanceof self) { - foreach ($this as $key => $val) { - $this->$key = $url->$key; - } - } - } - - - - /** - * Sets the scheme part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setScheme($value) - { - $this->updating(); - $this->scheme = (string) $value; - return $this; - } - - - - /** - * Returns the scheme part of URI. - * @return string - */ - public function getScheme() - { - return $this->scheme; - } - - - - /** - * Sets the user name part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setUser($value) - { - $this->updating(); - $this->user = (string) $value; - return $this; - } - - - - /** - * Returns the user name part of URI. - * @return string - */ - public function getUser() - { - return $this->user; - } - - - - /** - * Sets the password part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setPassword($value) - { - $this->updating(); - $this->pass = (string) $value; - return $this; - } - - - - /** - * Returns the password part of URI. - * @return string - */ - public function getPassword() - { - return $this->pass; - } - - - - /** - * Sets the host part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setHost($value) - { - $this->updating(); - $this->host = (string) $value; - return $this; - } - - - - /** - * Returns the host part of URI. - * @return string - */ - public function getHost() - { - return $this->host; - } - - - - /** - * Sets the port part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setPort($value) - { - $this->updating(); - $this->port = (int) $value; - return $this; - } - - - - /** - * Returns the port part of URI. - * @return string - */ - public function getPort() - { - return $this->port; - } - - - - /** - * Sets the path part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setPath($value) - { - $this->updating(); - $this->path = (string) $value; - return $this; - } - - - - /** - * Returns the path part of URI. - * @return string - */ - public function getPath() - { - return $this->path; - } - - - - /** - * Sets the query part of URI. - * @param string|array - * @return Url provides a fluent interface - */ - public function setQuery($value) - { - $this->updating(); - $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value); - return $this; - } - - - - /** - * Appends the query part of URI. - * @param string|array - * @return void - */ - public function appendQuery($value) - { - $this->updating(); - $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value); - $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value; - } - - - - /** - * Returns the query part of URI. - * @return string - */ - public function getQuery() - { - return $this->query; - } - - - - /** - * Sets the fragment part of URI. - * @param string - * @return Url provides a fluent interface - */ - public function setFragment($value) - { - $this->updating(); - $this->fragment = (string) $value; - return $this; - } - - - - /** - * Returns the fragment part of URI. - * @return string - */ - public function getFragment() - { - return $this->fragment; - } - - - - /** - * Returns the entire URI including query string and fragment. - * @return string - */ - public function getAbsoluteUrl() - { - return $this->scheme . '://' . $this->getAuthority() . $this->path - . ($this->query === '' ? '' : '?' . $this->query) - . ($this->fragment === '' ? '' : '#' . $this->fragment); - } - - - - /** - * Returns the [user[:pass]@]host[:port] part of URI. - * @return string - */ - public function getAuthority() - { - $authority = $this->host; - if ($this->port && isset(self::$defaultPorts[$this->scheme]) && $this->port !== self::$defaultPorts[$this->scheme]) { - $authority .= ':' . $this->port; - } - - if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') { - $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority; - } - - return $authority; - } - - - - /** - * Returns the scheme and authority part of URI. - * @return string - */ - public function getHostUrl() - { - return $this->scheme . '://' . $this->getAuthority(); - } - - - - /** - * Returns the base-path. - * @return string - */ - public function getBasePath() - { - $pos = strrpos($this->path, '/'); - return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1); - } - - - - /** - * Returns the base-URI. - * @return string - */ - public function getBaseUrl() - { - return $this->scheme . '://' . $this->getAuthority() . $this->getBasePath(); - } - - - - /** - * Returns the relative-URI. - * @return string - */ - public function getRelativeUrl() - { - return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl())); - } - - - - /** - * URI comparsion (this object must be in canonical form). - * @param string - * @return bool - */ - public function isEqual($url) - { - // compare host + path - $part = self::unescape(strtok($url, '?#'), '%/'); - if (strncmp($part, '//', 2) === 0) { // absolute URI without scheme - if ($part !== '//' . $this->getAuthority() . $this->path) { - return FALSE; - } - - } elseif (strncmp($part, '/', 1) === 0) { // absolute path - if ($part !== $this->path) { - return FALSE; - } - - } else { - if ($part !== $this->scheme . '://' . $this->getAuthority() . $this->path) { - return FALSE; - } - } - - // compare query strings - $part = preg_split('#[&;]#', self::unescape(strtr((string) strtok('?#'), '+', ' '), '%&;=+')); - sort($part); - $query = preg_split('#[&;]#', $this->query); - sort($query); - return $part === $query; - } - - - - /** - * Transform to canonical form. - * @return void - */ - public function canonicalize() - { - $this->updating(); - $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/'); - $this->host = strtolower(rawurldecode($this->host)); - $this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+'); - } - - - - /** - * @return string - */ - public function __toString() - { - return $this->getAbsoluteUrl(); - } - - - - /** - * Similar to rawurldecode, but preserve reserved chars encoded. - * @param string to decode - * @param string reserved characters - * @return string - */ - public static function unescape($s, $reserved = '%;/?:@&=+$,') - { - // reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," - // within a path segment, the characters "/", ";", "=", "?" are reserved - // within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved. - preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); - foreach (array_reverse($matches) as $match) { - $ch = chr(hexdec($match[0][0])); - if (strpos($reserved, $ch) === FALSE) { - $s = substr_replace($s, $ch, $match[0][1] - 1, 3); - } - } - return $s; - } - - - - /** @deprecated */ - function getRelativeUri() - { - trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getRelativeUrl() instead.', E_USER_WARNING); - return $this->getRelativeUrl(); - } - - /** @deprecated */ - function getAbsoluteUri() - { - trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getAbsoluteUrl() instead.', E_USER_WARNING); - return $this->getAbsoluteUrl(); - } - - /** @deprecated */ - function getHostUri() - { - trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getHostUrl() instead.', E_USER_WARNING); - return $this->getHostUrl(); - } - - /** @deprecated */ - function getBaseUri() - { - trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getBaseUrl() instead.', E_USER_WARNING); - return $this->getBaseUrl(); - } - -} + + * scheme user password host port basePath relativeUrl + * | | | | | | | + * /--\ /--\ /------\ /-------\ /--\/--\/----------------------------\ + * http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl + * \__________________________/\____________/^\________/^\______/ + * | | | | + * authority path query fragment + * + * + * - authority: [user[:password]@]host[:port] + * - hostUrl: http://user:password@nette.org:8042 + * - basePath: /en/ (everything before relative URI not including the script name) + * - baseUrl: http://user:password@nette.org:8042/en/ + * - relativeUrl: manual.php + * + * @author David Grudl + * + * @property string $scheme + * @property string $user + * @property string $password + * @property string $host + * @property string $port + * @property string $path + * @property string $query + * @property string $fragment + * @property-read string $absoluteUrl + * @property-read string $authority + * @property-read string $hostUrl + * @property-read string $basePath + * @property-read string $baseUrl + * @property-read string $relativeUrl + */ +class Url extends Nette\FreezableObject +{ + /** @var array */ + public static $defaultPorts = array( + 'http' => 80, + 'https' => 443, + 'ftp' => 21, + 'news' => 119, + 'nntp' => 119, + ); + + /** @var string */ + private $scheme = ''; + + /** @var string */ + private $user = ''; + + /** @var string */ + private $pass = ''; + + /** @var string */ + private $host = ''; + + /** @var int */ + private $port = NULL; + + /** @var string */ + private $path = ''; + + /** @var string */ + private $query = ''; + + /** @var string */ + private $fragment = ''; + + + + /** + * @param string URL + * @throws Nette\InvalidArgumentException + */ + public function __construct($url = NULL) + { + if (is_string($url)) { + $parts = @parse_url($url); // @ - is escalated to exception + if ($parts === FALSE) { + throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'."); + } + + foreach ($parts as $key => $val) { + $this->$key = $val; + } + + if (!$this->port && isset(self::$defaultPorts[$this->scheme])) { + $this->port = self::$defaultPorts[$this->scheme]; + } + + if ($this->path === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { + $this->path = '/'; + } + + } elseif ($url instanceof self) { + foreach ($this as $key => $val) { + $this->$key = $url->$key; + } + } + } + + + + /** + * Sets the scheme part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setScheme($value) + { + $this->updating(); + $this->scheme = (string) $value; + return $this; + } + + + + /** + * Returns the scheme part of URI. + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + + + /** + * Sets the user name part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setUser($value) + { + $this->updating(); + $this->user = (string) $value; + return $this; + } + + + + /** + * Returns the user name part of URI. + * @return string + */ + public function getUser() + { + return $this->user; + } + + + + /** + * Sets the password part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setPassword($value) + { + $this->updating(); + $this->pass = (string) $value; + return $this; + } + + + + /** + * Returns the password part of URI. + * @return string + */ + public function getPassword() + { + return $this->pass; + } + + + + /** + * Sets the host part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setHost($value) + { + $this->updating(); + $this->host = (string) $value; + return $this; + } + + + + /** + * Returns the host part of URI. + * @return string + */ + public function getHost() + { + return $this->host; + } + + + + /** + * Sets the port part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setPort($value) + { + $this->updating(); + $this->port = (int) $value; + return $this; + } + + + + /** + * Returns the port part of URI. + * @return string + */ + public function getPort() + { + return $this->port; + } + + + + /** + * Sets the path part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setPath($value) + { + $this->updating(); + $this->path = (string) $value; + return $this; + } + + + + /** + * Returns the path part of URI. + * @return string + */ + public function getPath() + { + return $this->path; + } + + + + /** + * Sets the query part of URI. + * @param string|array + * @return Url provides a fluent interface + */ + public function setQuery($value) + { + $this->updating(); + $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value); + return $this; + } + + + + /** + * Appends the query part of URI. + * @param string|array + * @return void + */ + public function appendQuery($value) + { + $this->updating(); + $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value); + $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value; + } + + + + /** + * Returns the query part of URI. + * @return string + */ + public function getQuery() + { + return $this->query; + } + + + + /** + * Sets the fragment part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setFragment($value) + { + $this->updating(); + $this->fragment = (string) $value; + return $this; + } + + + + /** + * Returns the fragment part of URI. + * @return string + */ + public function getFragment() + { + return $this->fragment; + } + + + + /** + * Returns the entire URI including query string and fragment. + * @return string + */ + public function getAbsoluteUrl() + { + return $this->scheme . '://' . $this->getAuthority() . $this->path + . ($this->query === '' ? '' : '?' . $this->query) + . ($this->fragment === '' ? '' : '#' . $this->fragment); + } + + + + /** + * Returns the [user[:pass]@]host[:port] part of URI. + * @return string + */ + public function getAuthority() + { + $authority = $this->host; + if ($this->port && isset(self::$defaultPorts[$this->scheme]) && $this->port !== self::$defaultPorts[$this->scheme]) { + $authority .= ':' . $this->port; + } + + if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') { + $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority; + } + + return $authority; + } + + + + /** + * Returns the scheme and authority part of URI. + * @return string + */ + public function getHostUrl() + { + return $this->scheme . '://' . $this->getAuthority(); + } + + + + /** + * Returns the base-path. + * @return string + */ + public function getBasePath() + { + $pos = strrpos($this->path, '/'); + return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1); + } + + + + /** + * Returns the base-URI. + * @return string + */ + public function getBaseUrl() + { + return $this->scheme . '://' . $this->getAuthority() . $this->getBasePath(); + } + + + + /** + * Returns the relative-URI. + * @return string + */ + public function getRelativeUrl() + { + return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl())); + } + + + + /** + * URI comparsion (this object must be in canonical form). + * @param string + * @return bool + */ + public function isEqual($url) + { + // compare host + path + $part = self::unescape(strtok($url, '?#'), '%/'); + if (strncmp($part, '//', 2) === 0) { // absolute URI without scheme + if ($part !== '//' . $this->getAuthority() . $this->path) { + return FALSE; + } + + } elseif (strncmp($part, '/', 1) === 0) { // absolute path + if ($part !== $this->path) { + return FALSE; + } + + } else { + if ($part !== $this->scheme . '://' . $this->getAuthority() . $this->path) { + return FALSE; + } + } + + // compare query strings + $part = preg_split('#[&;]#', self::unescape(strtr((string) strtok('?#'), '+', ' '), '%&;=+')); + sort($part); + $query = preg_split('#[&;]#', $this->query); + sort($query); + return $part === $query; + } + + + + /** + * Transform to canonical form. + * @return void + */ + public function canonicalize() + { + $this->updating(); + $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/'); + $this->host = strtolower(rawurldecode($this->host)); + $this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+'); + } + + + + /** + * @return string + */ + public function __toString() + { + return $this->getAbsoluteUrl(); + } + + + + /** + * Similar to rawurldecode, but preserve reserved chars encoded. + * @param string to decode + * @param string reserved characters + * @return string + */ + public static function unescape($s, $reserved = '%;/?:@&=+$,') + { + // reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," + // within a path segment, the characters "/", ";", "=", "?" are reserved + // within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved. + preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + foreach (array_reverse($matches) as $match) { + $ch = chr(hexdec($match[0][0])); + if (strpos($reserved, $ch) === FALSE) { + $s = substr_replace($s, $ch, $match[0][1] - 1, 3); + } + } + return $s; + } + + + + /** @deprecated */ + function getRelativeUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getRelativeUrl() instead.', E_USER_WARNING); + return $this->getRelativeUrl(); + } + + /** @deprecated */ + function getAbsoluteUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getAbsoluteUrl() instead.', E_USER_WARNING); + return $this->getAbsoluteUrl(); + } + + /** @deprecated */ + function getHostUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getHostUrl() instead.', E_USER_WARNING); + return $this->getHostUrl(); + } + + /** @deprecated */ + function getBaseUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getBaseUrl() instead.', E_USER_WARNING); + return $this->getBaseUrl(); + } + +} diff --git a/libs/Nette/Http/UrlScript.php b/libs/Nette/Http/UrlScript.php index 6dcc3fb..2aa641c 100644 --- a/libs/Nette/Http/UrlScript.php +++ b/libs/Nette/Http/UrlScript.php @@ -1,89 +1,89 @@ - - * http://nette.org/admin/script.php/pathinfo/?name=param#fragment - * \_______________/\________/ - * | | - * scriptPath pathInfo - * - * - * - scriptPath: /admin/script.php (or simply /admin/ when script is directory index) - * - pathInfo: /pathinfo/ (additional path information) - * - * @author David Grudl - * - * @property string $scriptPath - * @property-read string $pathInfo - */ -class UrlScript extends Url -{ - /** @var string */ - private $scriptPath = '/'; - - - - /** - * Sets the script-path part of URI. - * @param string - * @return UrlScript provides a fluent interface - */ - public function setScriptPath($value) - { - $this->updating(); - $this->scriptPath = (string) $value; - return $this; - } - - - - /** - * Returns the script-path part of URI. - * @return string - */ - public function getScriptPath() - { - return $this->scriptPath; - } - - - - /** - * Returns the base-path. - * @return string - */ - public function getBasePath() - { - $pos = strrpos($this->scriptPath, '/'); - return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1); - } - - - - /** - * Returns the additional path information. - * @return string - */ - public function getPathInfo() - { - return (string) substr($this->path, strlen($this->scriptPath)); - } - -} + + * http://nette.org/admin/script.php/pathinfo/?name=param#fragment + * \_______________/\________/ + * | | + * scriptPath pathInfo + * + * + * - scriptPath: /admin/script.php (or simply /admin/ when script is directory index) + * - pathInfo: /pathinfo/ (additional path information) + * + * @author David Grudl + * + * @property string $scriptPath + * @property-read string $pathInfo + */ +class UrlScript extends Url +{ + /** @var string */ + private $scriptPath = '/'; + + + + /** + * Sets the script-path part of URI. + * @param string + * @return UrlScript provides a fluent interface + */ + public function setScriptPath($value) + { + $this->updating(); + $this->scriptPath = (string) $value; + return $this; + } + + + + /** + * Returns the script-path part of URI. + * @return string + */ + public function getScriptPath() + { + return $this->scriptPath; + } + + + + /** + * Returns the base-path. + * @return string + */ + public function getBasePath() + { + $pos = strrpos($this->scriptPath, '/'); + return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1); + } + + + + /** + * Returns the additional path information. + * @return string + */ + public function getPathInfo() + { + return (string) substr($this->path, strlen($this->scriptPath)); + } + +} diff --git a/libs/Nette/Http/User.php b/libs/Nette/Http/User.php deleted file mode 100644 index c882688..0000000 --- a/libs/Nette/Http/User.php +++ /dev/null @@ -1,403 +0,0 @@ -context = $context; - } - - - - /********************* Authentication ****************d*g**/ - - - - /** - * Conducts the authentication process. Parameters are optional. - * @param mixed optional parameter (e.g. username) - * @param mixed optional parameter (e.g. password) - * @return void - * @throws Nette\Security\AuthenticationException if authentication was not successful - */ - public function login($username = NULL, $password = NULL) - { - $this->logout(TRUE); - $credentials = func_get_args(); - $this->setIdentity($this->context->authenticator->authenticate($credentials)); - $this->setAuthenticated(TRUE); - $this->onLoggedIn($this); - } - - - - /** - * Logs out the user from the current session. - * @param bool clear the identity from persistent storage? - * @return void - */ - final public function logout($clearIdentity = FALSE) - { - if ($this->isLoggedIn()) { - $this->setAuthenticated(FALSE); - $this->onLoggedOut($this); - } - - if ($clearIdentity) { - $this->setIdentity(NULL); - } - } - - - - /** - * Is this user authenticated? - * @return bool - */ - final public function isLoggedIn() - { - $session = $this->getSessionNamespace(FALSE); - return $session && $session->authenticated; - } - - - - /** - * Returns current user identity, if any. - * @return Nette\Security\IIdentity - */ - final public function getIdentity() - { - $session = $this->getSessionNamespace(FALSE); - return $session ? $session->identity : NULL; - } - - - - /** - * Returns current user ID, if any. - * @return mixed - */ - public function getId() - { - $identity = $this->getIdentity(); - return $identity ? $identity->getId() : NULL; - } - - - - /** - * Sets authentication handler. - * @param Nette\Security\IAuthenticator - * @return User provides a fluent interface - */ - public function setAuthenticationHandler(IAuthenticator $handler) - { - $this->context->authenticator = $handler; - return $this; - } - - - - /** - * Returns authentication handler. - * @return Nette\Security\IAuthenticator - */ - final public function getAuthenticationHandler() - { - return $this->context->authenticator; - } - - - - /** - * Changes namespace; allows more users to share a session. - * @param string - * @return User provides a fluent interface - */ - public function setNamespace($namespace) - { - if ($this->namespace !== $namespace) { - $this->namespace = (string) $namespace; - $this->session = NULL; - } - return $this; - } - - - - /** - * Returns current namespace. - * @return string - */ - final public function getNamespace() - { - return $this->namespace; - } - - - - /** - * Enables log out after inactivity. - * @param string|int|DateTime number of seconds or timestamp - * @param bool log out when the browser is closed? - * @param bool clear the identity from persistent storage? - * @return User provides a fluent interface - */ - public function setExpiration($time, $whenBrowserIsClosed = TRUE, $clearIdentity = FALSE) - { - $session = $this->getSessionNamespace(TRUE); - if ($time) { - $time = Nette\DateTime::from($time)->format('U'); - $session->expireTime = $time; - $session->expireDelta = $time - time(); - - } else { - unset($session->expireTime, $session->expireDelta); - } - - $session->expireIdentity = (bool) $clearIdentity; - $session->expireBrowser = (bool) $whenBrowserIsClosed; - $session->browserCheck = TRUE; - $session->setExpiration(0, 'browserCheck'); - return $this; - } - - - - /** - * Why was user logged out? - * @return int - */ - final public function getLogoutReason() - { - $session = $this->getSessionNamespace(FALSE); - return $session ? $session->reason : NULL; - } - - - - /** - * Returns and initializes $this->session. - * @return SessionNamespace - */ - protected function getSessionNamespace($need) - { - if ($this->session !== NULL) { - return $this->session; - } - - if (!$need && !$this->context->session->exists()) { - return NULL; - } - - $this->session = $session = $this->context->session->getNamespace('Nette.Web.User/' . $this->namespace); - - if (!$session->identity instanceof IIdentity || !is_bool($session->authenticated)) { - $session->remove(); - } - - if ($session->authenticated && $session->expireBrowser && !$session->browserCheck) { // check if browser was closed? - $session->reason = self::BROWSER_CLOSED; - $session->authenticated = FALSE; - $this->onLoggedOut($this); - if ($session->expireIdentity) { - unset($session->identity); - } - } - - if ($session->authenticated && $session->expireDelta > 0) { // check time expiration - if ($session->expireTime < time()) { - $session->reason = self::INACTIVITY; - $session->authenticated = FALSE; - $this->onLoggedOut($this); - if ($session->expireIdentity) { - unset($session->identity); - } - } - $session->expireTime = time() + $session->expireDelta; // sliding expiration - } - - if (!$session->authenticated) { - unset($session->expireTime, $session->expireDelta, $session->expireIdentity, - $session->expireBrowser, $session->browserCheck, $session->authTime); - } - - return $this->session; - } - - - - /** - * Sets the authenticated status of this user. - * @param bool flag indicating the authenticated status of user - * @return User provides a fluent interface - */ - protected function setAuthenticated($state) - { - $session = $this->getSessionNamespace(TRUE); - $session->authenticated = (bool) $state; - - // Session Fixation defence - $this->context->session->regenerateId(); - - if ($state) { - $session->reason = NULL; - $session->authTime = time(); // informative value - - } else { - $session->reason = self::MANUAL; - $session->authTime = NULL; - } - return $this; - } - - - - /** - * Sets the user identity. - * @param Nette\Security\IIdentity - * @return User provides a fluent interface - */ - protected function setIdentity(IIdentity $identity = NULL) - { - $this->getSessionNamespace(TRUE)->identity = $identity; - return $this; - } - - - - /********************* Authorization ****************d*g**/ - - - - /** - * Returns a list of effective roles that a user has been granted. - * @return array - */ - public function getRoles() - { - if (!$this->isLoggedIn()) { - return array($this->guestRole); - } - - $identity = $this->getIdentity(); - return $identity ? $identity->getRoles() : array($this->authenticatedRole); - } - - - - /** - * Is a user in the specified effective role? - * @param string - * @return bool - */ - final public function isInRole($role) - { - return in_array($role, $this->getRoles(), TRUE); - } - - - - /** - * Has a user effective access to the Resource? - * If $resource is NULL, then the query applies to all resources. - * @param string resource - * @param string privilege - * @return bool - */ - public function isAllowed($resource = IAuthorizator::ALL, $privilege = IAuthorizator::ALL) - { - $authorizator = $this->context->authorizator; - foreach ($this->getRoles() as $role) { - if ($authorizator->isAllowed($role, $resource, $privilege)) { - return TRUE; - } - } - - return FALSE; - } - - - - /** - * Sets authorization handler. - * @param Nette\Security\IAuthorizator - * @return User provides a fluent interface - */ - public function setAuthorizationHandler(IAuthorizator $handler) - { - $this->context->authorizator = $handler; - return $this; - } - - - - /** - * Returns current authorization handler. - * @return Nette\Security\IAuthorizator - */ - final public function getAuthorizationHandler() - { - return $this->context->authorizator; - } - -} diff --git a/libs/Nette/Http/UserStorage.php b/libs/Nette/Http/UserStorage.php new file mode 100644 index 0000000..50978ef --- /dev/null +++ b/libs/Nette/Http/UserStorage.php @@ -0,0 +1,220 @@ +sessionHandler = $sessionHandler; + } + + + + /** + * Sets the authenticated status of this user. + * @param bool + * @return UserStorage Provides a fluent interface + */ + public function setAuthenticated($state) + { + $section = $this->getSessionSection(TRUE); + $section->authenticated = (bool) $state; + + // Session Fixation defence + $this->sessionHandler->regenerateId(); + + if ($state) { + $section->reason = NULL; + $section->authTime = time(); // informative value + + } else { + $section->reason = self::MANUAL; + $section->authTime = NULL; + } + return $this; + } + + + + /** + * Is this user authenticated? + * @return bool + */ + public function isAuthenticated() + { + $session = $this->getSessionSection(FALSE); + return $session && $session->authenticated; + } + + + + /** + * Sets the user identity. + * @return UserStorage Provides a fluent interface + */ + public function setIdentity(IIdentity $identity = NULL) + { + $this->getSessionSection(TRUE)->identity = $identity; + return $this; + } + + + + /** + * Returns current user identity, if any. + * @return Nette\Security\IIdentity|NULL + */ + public function getIdentity() + { + $session = $this->getSessionSection(FALSE); + return $session ? $session->identity : NULL; + } + + + + /** + * Changes namespace; allows more users to share a session. + * @param string + * @return UserStorage Provides a fluent interface + */ + public function setNamespace($namespace) + { + if ($this->namespace !== $namespace) { + $this->namespace = (string) $namespace; + $this->sessionSection = NULL; + } + return $this; + } + + + + /** + * Returns current namespace. + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + + + /** + * Enables log out after inactivity. + * @param string|int|DateTime Number of seconds or timestamp + * @param int Log out when the browser is closed | Clear the identity from persistent storage? + * @return UserStorage Provides a fluent interface + */ + public function setExpiration($time, $flags = 0) + { + $section = $this->getSessionSection(TRUE); + if ($time) { + $time = Nette\DateTime::from($time)->format('U'); + $section->expireTime = $time; + $section->expireDelta = $time - time(); + + } else { + unset($section->expireTime, $section->expireDelta); + } + + $section->expireIdentity = (bool) ($flags & self::CLEAR_IDENTITY); + $section->expireBrowser = (bool) ($flags & self::BROWSER_CLOSED); + $section->browserCheck = TRUE; + $section->setExpiration(0, 'browserCheck'); + $section->setExpiration($time, 'foo'); // time check + return $this; + } + + + + /** + * Why was user logged out? + * @return int + */ + public function getLogoutReason() + { + $session = $this->getSessionSection(FALSE); + return $session ? $session->reason : NULL; + } + + + + /** + * Returns and initializes $this->sessionSection. + * @return SessionSection + */ + protected function getSessionSection($need) + { + if ($this->sessionSection !== NULL) { + return $this->sessionSection; + } + + if (!$need && !$this->sessionHandler->exists()) { + return NULL; + } + + $this->sessionSection = $section = $this->sessionHandler->getSection('Nette.Http.UserStorage/' . $this->namespace); + + if (!$section->identity instanceof IIdentity || !is_bool($section->authenticated)) { + $section->remove(); + } + + if ($section->authenticated && $section->expireBrowser && !$section->browserCheck) { // check if browser was closed? + $section->reason = self::BROWSER_CLOSED; + $section->authenticated = FALSE; + if ($section->expireIdentity) { + unset($section->identity); + } + } + + if ($section->authenticated && $section->expireDelta > 0) { // check time expiration + if ($section->expireTime < time()) { + $section->reason = self::INACTIVITY; + $section->authenticated = FALSE; + if ($section->expireIdentity) { + unset($section->identity); + } + } + $section->expireTime = time() + $section->expireDelta; // sliding expiration + } + + if (!$section->authenticated) { + unset($section->expireTime, $section->expireDelta, $section->expireIdentity, + $section->expireBrowser, $section->browserCheck, $section->authTime); + } + + return $this->sessionSection; + } + +} diff --git a/libs/Nette/Iterators/CachingIterator.php b/libs/Nette/Iterators/CachingIterator.php index 0f991fa..d97c811 100644 --- a/libs/Nette/Iterators/CachingIterator.php +++ b/libs/Nette/Iterators/CachingIterator.php @@ -1,260 +1,266 @@ -getIterator(); - - } elseif (!$iterator instanceof \Iterator) { - $iterator = new \IteratorIterator($iterator); - } - - } else { - throw new Nette\InvalidArgumentException("Invalid argument passed to foreach resp. " . __CLASS__ . "; array or Traversable expected, " . (is_object($iterator) ? get_class($iterator) : gettype($iterator)) ." given."); - } - - parent::__construct($iterator, 0); - } - - - - /** - * Is the current element the first one? - * @param int grid width - * @return bool - */ - public function isFirst($width = NULL) - { - return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0); - } - - - - /** - * Is the current element the last one? - * @param int grid width - * @return bool - */ - public function isLast($width = NULL) - { - return !$this->hasNext() || ($width && ($this->counter % $width) === 0); - } - - - - /** - * Is the iterator empty? - * @return bool - */ - public function isEmpty() - { - return $this->counter === 0; - } - - - - /** - * Is the counter odd? - * @return bool - */ - public function isOdd() - { - return $this->counter % 2 === 1; - } - - - - /** - * Is the counter even? - * @return bool - */ - public function isEven() - { - return $this->counter % 2 === 0; - } - - - - /** - * Returns the counter. - * @return int - */ - public function getCounter() - { - return $this->counter; - } - - - - /** - * Returns the count of elements. - * @return int - */ - public function count() - { - $inner = $this->getInnerIterator(); - if ($inner instanceof \Countable) { - return $inner->count(); - - } else { - throw new Nette\NotSupportedException('Iterator is not countable.'); - } - } - - - - /** - * Forwards to the next element. - * @return void - */ - public function next() - { - parent::next(); - if (parent::valid()) { - $this->counter++; - } - } - - - - /** - * Rewinds the Iterator. - * @return void - */ - public function rewind() - { - parent::rewind(); - $this->counter = parent::valid() ? 1 : 0; - } - - - - /** - * Returns the next key. - * @return mixed - */ - public function getNextKey() - { - return $this->getInnerIterator()->key(); - } - - - - /** - * Returns the next element. - * @return mixed - */ - public function getNextValue() - { - return $this->getInnerIterator()->current(); - } - - - - /********************* Nette\Object behaviour ****************d*g**/ - - - - /** - * Call to undefined method. - * @param string method name - * @param array arguments - * @return mixed - * @throws Nette\MemberAccessException - */ - public function __call($name, $args) - { - return Nette\ObjectMixin::call($this, $name, $args); - } - - - - /** - * Returns property value. Do not call directly. - * @param string property name - * @return mixed property value - * @throws Nette\MemberAccessException if the property is not defined. - */ - public function &__get($name) - { - return Nette\ObjectMixin::get($this, $name); - } - - - - /** - * Sets value of a property. Do not call directly. - * @param string property name - * @param mixed property value - * @return void - * @throws Nette\MemberAccessException if the property is not defined or is read-only - */ - public function __set($name, $value) - { - return Nette\ObjectMixin::set($this, $name, $value); - } - - - - /** - * Is property defined? - * @param string property name - * @return bool - */ - public function __isset($name) - { - return Nette\ObjectMixin::has($this, $name); - } - - - - /** - * Access to undeclared property. - * @param string property name - * @return void - * @throws Nette\MemberAccessException - */ - public function __unset($name) - { - Nette\ObjectMixin::remove($this, $name); - } - - -} +getIterator(); + + } elseif (!$iterator instanceof \Iterator) { + $iterator = new \IteratorIterator($iterator); + } + + } else { + throw new Nette\InvalidArgumentException("Invalid argument passed to foreach resp. " . __CLASS__ . "; array or Traversable expected, " . (is_object($iterator) ? get_class($iterator) : gettype($iterator)) ." given."); + } + + parent::__construct($iterator, 0); + } + + + + /** + * Is the current element the first one? + * @param int grid width + * @return bool + */ + public function isFirst($width = NULL) + { + return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0); + } + + + + /** + * Is the current element the last one? + * @param int grid width + * @return bool + */ + public function isLast($width = NULL) + { + return !$this->hasNext() || ($width && ($this->counter % $width) === 0); + } + + + + /** + * Is the iterator empty? + * @return bool + */ + public function isEmpty() + { + return $this->counter === 0; + } + + + + /** + * Is the counter odd? + * @return bool + */ + public function isOdd() + { + return $this->counter % 2 === 1; + } + + + + /** + * Is the counter even? + * @return bool + */ + public function isEven() + { + return $this->counter % 2 === 0; + } + + + + /** + * Returns the counter. + * @return int + */ + public function getCounter() + { + return $this->counter; + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + $inner = $this->getInnerIterator(); + if ($inner instanceof \Countable) { + return $inner->count(); + + } else { + throw new Nette\NotSupportedException('Iterator is not countable.'); + } + } + + + + /** + * Forwards to the next element. + * @return void + */ + public function next() + { + parent::next(); + if (parent::valid()) { + $this->counter++; + } + } + + + + /** + * Rewinds the Iterator. + * @return void + */ + public function rewind() + { + parent::rewind(); + $this->counter = parent::valid() ? 1 : 0; + } + + + + /** + * Returns the next key. + * @return mixed + */ + public function getNextKey() + { + return $this->getInnerIterator()->key(); + } + + + + /** + * Returns the next element. + * @return mixed + */ + public function getNextValue() + { + return $this->getInnerIterator()->current(); + } + + + + /********************* Nette\Object behaviour ****************d*g**/ + + + + /** + * Call to undefined method. + * @param string method name + * @param array arguments + * @return mixed + * @throws Nette\MemberAccessException + */ + public function __call($name, $args) + { + return Nette\ObjectMixin::call($this, $name, $args); + } + + + + /** + * Returns property value. Do not call directly. + * @param string property name + * @return mixed property value + * @throws Nette\MemberAccessException if the property is not defined. + */ + public function &__get($name) + { + return Nette\ObjectMixin::get($this, $name); + } + + + + /** + * Sets value of a property. Do not call directly. + * @param string property name + * @param mixed property value + * @return void + * @throws Nette\MemberAccessException if the property is not defined or is read-only + */ + public function __set($name, $value) + { + return Nette\ObjectMixin::set($this, $name, $value); + } + + + + /** + * Is property defined? + * @param string property name + * @return bool + */ + public function __isset($name) + { + return Nette\ObjectMixin::has($this, $name); + } + + + + /** + * Access to undeclared property. + * @param string property name + * @return void + * @throws Nette\MemberAccessException + */ + public function __unset($name) + { + Nette\ObjectMixin::remove($this, $name); + } + + +} diff --git a/libs/Nette/Iterators/Filter.php b/libs/Nette/Iterators/Filter.php index 146ad7c..a31fb83 100644 --- a/libs/Nette/Iterators/Filter.php +++ b/libs/Nette/Iterators/Filter.php @@ -1,47 +1,42 @@ -callback = $callback; - } - - - - public function accept() - { - return call_user_func($this->callback, $this); - } - -} +callback = new Nette\Callback($callback); + } + + + + public function accept() + { + return $this->callback->invoke($this); + } + +} diff --git a/libs/Nette/Iterators/InstanceFilter.php b/libs/Nette/Iterators/InstanceFilter.php index 23af3c8..dbbc376 100644 --- a/libs/Nette/Iterators/InstanceFilter.php +++ b/libs/Nette/Iterators/InstanceFilter.php @@ -1,62 +1,62 @@ -type = $type; - parent::__construct($iterator); - } - - - - /** - * Expose the current element of the inner iterator? - * @return bool - */ - public function accept() - { - return $this->current() instanceof $this->type; - } - - - - /** - * Returns the count of elements. - * @return int - */ - public function count() - { - return iterator_count($this); - } - -} +type = $type; + parent::__construct($iterator); + } + + + + /** + * Expose the current element of the inner iterator? + * @return bool + */ + public function accept() + { + return $this->current() instanceof $this->type; + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + return iterator_count($this); + } + +} diff --git a/libs/Nette/Iterators/Mapper.php b/libs/Nette/Iterators/Mapper.php index b12882d..62dd6ec 100644 --- a/libs/Nette/Iterators/Mapper.php +++ b/libs/Nette/Iterators/Mapper.php @@ -1,47 +1,42 @@ -callback = $callback; - } - - - - public function current() - { - return call_user_func($this->callback, parent::current(), parent::key()); - } - -} +callback = new Nette\Callback($callback); + } + + + + public function current() + { + return $this->callback->invoke(parent::current(), parent::key()); + } + +} diff --git a/libs/Nette/Iterators/RecursiveFilter.php b/libs/Nette/Iterators/RecursiveFilter.php index d3faf0f..f101d86 100644 --- a/libs/Nette/Iterators/RecursiveFilter.php +++ b/libs/Nette/Iterators/RecursiveFilter.php @@ -1,66 +1,61 @@ -callback = $callback; - $this->childrenCallback = $childrenCallback; - } - - - - public function accept() - { - return $this->callback === NULL || call_user_func($this->callback, $this); - } - - - - public function hasChildren() - { - return $this->getInnerIterator()->hasChildren() - && ($this->childrenCallback === NULL || call_user_func($this->childrenCallback, $this)); - } - - - - public function getChildren() - { - return new static($this->getInnerIterator()->getChildren(), $this->callback, $this->childrenCallback); - } - -} +callback = $callback === NULL ? NULL : new Nette\Callback($callback); + $this->childrenCallback = $childrenCallback === NULL ? NULL : new Nette\Callback($childrenCallback); + } + + + + public function accept() + { + return $this->callback === NULL || $this->callback->invoke($this); + } + + + + public function hasChildren() + { + return $this->getInnerIterator()->hasChildren() + && ($this->childrenCallback === NULL || $this->childrenCallback->invoke($this)); + } + + + + public function getChildren() + { + return new static($this->getInnerIterator()->getChildren(), $this->callback, $this->childrenCallback); + } + +} diff --git a/libs/Nette/Iterators/Recursor.php b/libs/Nette/Iterators/Recursor.php index daee291..a300eae 100644 --- a/libs/Nette/Iterators/Recursor.php +++ b/libs/Nette/Iterators/Recursor.php @@ -1,60 +1,60 @@ -current(); - return ($obj instanceof \IteratorAggregate && $obj->getIterator() instanceof \RecursiveIterator) - || $obj instanceof \RecursiveIterator; - } - - - - /** - * The sub-iterator for the current element. - * @return \RecursiveIterator - */ - public function getChildren() - { - $obj = $this->current(); - return $obj instanceof \IteratorAggregate ? $obj->getIterator() : $obj; - } - - - - /** - * Returns the count of elements. - * @return int - */ - public function count() - { - return iterator_count($this); - } - -} +current(); + return ($obj instanceof \IteratorAggregate && $obj->getIterator() instanceof \RecursiveIterator) + || $obj instanceof \RecursiveIterator; + } + + + + /** + * The sub-iterator for the current element. + * @return \RecursiveIterator + */ + public function getChildren() + { + $obj = $this->current(); + return $obj instanceof \IteratorAggregate ? $obj->getIterator() : $obj; + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + return iterator_count($this); + } + +} diff --git a/libs/Nette/Latte/Compiler.php b/libs/Nette/Latte/Compiler.php new file mode 100644 index 0000000..0de0fc1 --- /dev/null +++ b/libs/Nette/Latte/Compiler.php @@ -0,0 +1,530 @@ + IMacro[]] */ + private $macros; + + /** @var \SplObjectStorage */ + private $macroHandlers; + + /** @var HtmlNode[] */ + private $htmlNodes = array(); + + /** @var MacroNode[] */ + private $macroNodes = array(); + + /** @var string[] */ + private $attrCodes = array(); + + /** @var string */ + private $contentType; + + /** @var array [context, subcontext] */ + private $context; + + /** @var string */ + private $templateId; + + /** Context-aware escaping content types */ + const CONTENT_HTML = 'html', + CONTENT_XHTML = 'xhtml', + CONTENT_XML = 'xml', + CONTENT_JS = 'js', + CONTENT_CSS = 'css', + CONTENT_ICAL = 'ical', + CONTENT_TEXT = 'text'; + + /** @internal Context-aware escaping HTML contexts */ + const CONTEXT_COMMENT = 'comment', + CONTEXT_SINGLE_QUOTED = "'", + CONTEXT_DOUBLE_QUOTED = '"'; + + + public function __construct() + { + $this->macroHandlers = new \SplObjectStorage; + } + + + + /** + * Adds new macro. + * @param string + * @return Compiler provides a fluent interface + */ + public function addMacro($name, IMacro $macro) + { + $this->macros[$name][] = $macro; + $this->macroHandlers->attach($macro); + return $this; + } + + + + /** + * Compiles tokens to PHP code. + * @param Token[] + * @return string + */ + public function compile(array $tokens) + { + $this->templateId = Strings::random(); + $this->tokens = $tokens; + $output = ''; + $this->output = & $output; + $this->htmlNodes = $this->macroNodes = array(); + $this->setContentType($this->defaultContentType); + + foreach ($this->macroHandlers as $handler) { + $handler->initialize($this); + } + + try { + foreach ($tokens as $this->position => $token) { + if ($token->type === Token::TEXT) { + $this->output .= $token->text; + + } elseif ($token->type === Token::MACRO_TAG) { + $isRightmost = !isset($tokens[$this->position + 1]) + || substr($tokens[$this->position + 1]->text, 0, 1) === "\n"; + $this->writeMacro($token->name, $token->value, $token->modifiers, $isRightmost); + + } elseif ($token->type === Token::HTML_TAG_BEGIN) { + $this->processHtmlTagBegin($token); + + } elseif ($token->type === Token::HTML_TAG_END) { + $this->processHtmlTagEnd($token); + + } elseif ($token->type === Token::HTML_ATTRIBUTE) { + $this->processHtmlAttribute($token); + + } elseif ($token->type === Token::COMMENT) { + $this->processComment($token); + } + } + } catch (CompileException $e) { + $e->sourceLine = $token->line; + throw $e; + } + + + foreach ($this->htmlNodes as $htmlNode) { + if (!empty($htmlNode->macroAttrs)) { + throw new CompileException("Missing end tag name> for macro-attribute " . Parser::N_PREFIX + . implode(' and ' . Parser::N_PREFIX, array_keys($htmlNode->macroAttrs)) . ".", 0, $token->line); + } + } + + $prologs = $epilogs = ''; + foreach ($this->macroHandlers as $handler) { + $res = $handler->finalize(); + $handlerName = get_class($handler); + $prologs .= empty($res[0]) ? '' : ""; + $epilogs = (empty($res[1]) ? '' : "") . $epilogs; + } + $output = ($prologs ? $prologs . "\n" : '') . $output . $epilogs; + + if ($this->macroNodes) { + throw new CompileException("There are unclosed macros.", 0, $token->line); + } + + $output = $this->expandTokens($output); + return $output; + } + + + + /** + * @return Compiler provides a fluent interface + */ + public function setContentType($type) + { + $this->contentType = $type; + $this->context = NULL; + return $this; + } + + + + /** + * @return string + */ + public function getContentType() + { + return $this->contentType; + } + + + + /** + * @return Compiler provides a fluent interface + */ + public function setContext($context, $sub = NULL) + { + $this->context = array($context, $sub); + return $this; + } + + + + /** + * @return array [context, subcontext] + */ + public function getContext() + { + return $this->context; + } + + + + /** + * @return string + */ + public function getTemplateId() + { + return $this->templateId; + } + + + + /** + * Returns current line number. + * @return int + */ + public function getLine() + { + return $this->tokens ? $this->tokens[$this->position]->line : NULL; + } + + + + public function expandTokens($s) + { + return strtr($s, $this->attrCodes); + } + + + + private function processHtmlTagBegin(Token $token) + { + if ($token->closing) { + do { + $htmlNode = array_pop($this->htmlNodes); + if (!$htmlNode) { + $htmlNode = new HtmlNode($token->name); + } + if (strcasecmp($htmlNode->name, $token->name) === 0) { + break; + } + if ($htmlNode->macroAttrs) { + throw new CompileException("Unexpected name>.", 0, $token->line); + } + } while (TRUE); + $this->htmlNodes[] = $htmlNode; + $htmlNode->closing = TRUE; + $htmlNode->offset = strlen($this->output); + $this->setContext(NULL); + + } elseif ($token->text === '') { + $this->output .= $token->text; + $this->setContext(NULL); + return; + } + + $htmlNode = end($this->htmlNodes); + $isEmpty = !$htmlNode->closing && (Strings::contains($token->text, '/') || $htmlNode->isEmpty); + + if ($isEmpty && in_array($this->contentType, array(self::CONTENT_HTML, self::CONTENT_XHTML))) { // auto-correct + $token->text = preg_replace('#^.*>#', $this->contentType === self::CONTENT_XHTML ? ' />' : '>', $token->text); + } + + if (empty($htmlNode->macroAttrs)) { + $this->output .= $token->text; + } else { + $code = substr($this->output, $htmlNode->offset) . $token->text; + $this->output = substr($this->output, 0, $htmlNode->offset); + $this->writeAttrsMacro($code, $htmlNode); + if ($isEmpty) { + $htmlNode->closing = TRUE; + $this->writeAttrsMacro('', $htmlNode); + } + } + + if ($isEmpty) { + $htmlNode->closing = TRUE; + } + + if (!$htmlNode->closing && (strcasecmp($htmlNode->name, 'script') === 0 || strcasecmp($htmlNode->name, 'style') === 0)) { + $this->setContext(strcasecmp($htmlNode->name, 'style') ? self::CONTENT_JS : self::CONTENT_CSS); + } else { + $this->setContext(NULL); + if ($htmlNode->closing) { + array_pop($this->htmlNodes); + } + } + } + + + + private function processHtmlAttribute(Token $token) + { + $htmlNode = end($this->htmlNodes); + if (Strings::startsWith($token->name, Parser::N_PREFIX)) { + $name = substr($token->name, strlen(Parser::N_PREFIX)); + if (isset($htmlNode->macroAttrs[$name])) { + throw new CompileException("Found multiple macro-attributes $token->name.", 0, $token->line); + } + $htmlNode->macroAttrs[$name] = $token->value; + + } else { + $htmlNode->attrs[$token->name] = TRUE; + $this->output .= $token->text; + if ($token->value) { // quoted + $context = NULL; + if (strncasecmp($token->name, 'on', 2) === 0) { + $context = self::CONTENT_JS; + } elseif ($token->name === 'style') { + $context = self::CONTENT_CSS; + } + $this->setContext($token->value, $context); + } + } + } + + + + private function processComment(Token $token) + { + $isLeftmost = trim(substr($this->output, strrpos("\n$this->output", "\n"))) === ''; + if (!$isLeftmost) { + $this->output .= substr($token->text, strlen(rtrim($token->text, "\n"))); + } + } + + + + /********************* macros ****************d*g**/ + + + + /** + * Generates code for {macro ...} to the output. + * @param string + * @param string + * @param string + * @param bool + * @return MacroNode + */ + public function writeMacro($name, $args = NULL, $modifiers = NULL, $isRightmost = FALSE, HtmlNode $htmlNode = NULL, $prefix = NULL) + { + if ($name[0] === '/') { // closing + $node = end($this->macroNodes); + + if (!$node || ("/$node->name" !== $name && '/' !== $name) || $modifiers + || ($args && $node->args && !Strings::startsWith("$node->args ", "$args ")) + ) { + $name .= $args ? ' ' : ''; + throw new CompileException("Unexpected macro {{$name}{$args}{$modifiers}}" + . ($node ? ", expecting {/$node->name}" . ($args && $node->args ? " or eventually {/$node->name $node->args}" : '') : '')); + } + + array_pop($this->macroNodes); + if (!$node->args) { + $node->setArgs($args); + } + + $isLeftmost = $node->content ? trim(substr($this->output, strrpos("\n$this->output", "\n"))) === '' : FALSE; + + $node->closing = TRUE; + $node->macro->nodeClosed($node); + + $this->output = & $node->saved[0]; + $this->writeCode($node->openingCode, $this->output, $node->saved[1]); + $this->writeCode($node->closingCode, $node->content, $isRightmost, $isLeftmost); + $this->output .= $node->content; + + } else { // opening + $node = $this->expandMacro($name, $args, $modifiers, $htmlNode, $prefix); + if ($node->isEmpty) { + $this->writeCode($node->openingCode, $this->output, $isRightmost); + + } else { + $this->macroNodes[] = $node; + $node->saved = array(& $this->output, $isRightmost); + $this->output = & $node->content; + } + } + return $node; + } + + + + private function writeCode($code, & $output, $isRightmost, $isLeftmost = NULL) + { + if ($isRightmost) { + $leftOfs = strrpos("\n$output", "\n"); + $isLeftmost = $isLeftmost === NULL ? trim(substr($output, $leftOfs)) === '' : $isLeftmost; + if ($isLeftmost && substr($code, 0, 11) !== ' remove indentation + } elseif (substr($code, -2) === '?>') { + $code .= "\n"; // double newline to avoid newline eating by PHP + } + } + $output .= $code; + } + + + + /** + * Generates code for macro to the output. + * @param string + * @return void + */ + public function writeAttrsMacro($code, HtmlNode $htmlNode) + { + $attrs = $htmlNode->macroAttrs; + $left = $right = array(); + $attrCode = ''; + + foreach ($this->macros as $name => $foo) { + $attrName = MacroNode::PREFIX_INNER . "-$name"; + if (isset($attrs[$attrName])) { + if ($htmlNode->closing) { + $left[] = array("/$name", '', MacroNode::PREFIX_INNER); + } else { + array_unshift($right, array($name, $attrs[$attrName], MacroNode::PREFIX_INNER)); + } + unset($attrs[$attrName]); + } + } + + foreach (array_reverse($this->macros) as $name => $foo) { + $attrName = MacroNode::PREFIX_TAG . "-$name"; + if (isset($attrs[$attrName])) { + $left[] = array($name, $attrs[$attrName], MacroNode::PREFIX_TAG); + array_unshift($right, array("/$name", '', MacroNode::PREFIX_TAG)); + unset($attrs[$attrName]); + } + } + + foreach ($this->macros as $name => $foo) { + if (isset($attrs[$name])) { + if ($htmlNode->closing) { + $right[] = array("/$name", '', NULL); + } else { + array_unshift($left, array($name, $attrs[$name], NULL)); + } + unset($attrs[$name]); + } + } + + if ($attrs) { + throw new CompileException("Unknown macro-attribute " . Parser::N_PREFIX + . implode(' and ' . Parser::N_PREFIX, array_keys($attrs))); + } + + if (!$htmlNode->closing) { + $htmlNode->attrCode = & $this->attrCodes[$uniq = ' n:' . Nette\Utils\Strings::random()]; + $code = substr_replace($code, $uniq, strrpos($code, '/>') ?: strrpos($code, '>'), 0); + } + + foreach ($left as $item) { + $node = $this->writeMacro($item[0], $item[1], NULL, NULL, $htmlNode, $item[2]); + if ($node->closing || $node->isEmpty) { + $htmlNode->attrCode .= $node->attrCode; + if ($node->isEmpty) { + unset($htmlNode->macroAttrs[$node->name]); + } + } + } + + $this->output .= $code; + + foreach ($right as $item) { + $node = $this->writeMacro($item[0], $item[1], NULL, NULL, $htmlNode); + if ($node->closing) { + $htmlNode->attrCode .= $node->attrCode; + } + } + + if ($right && substr($this->output, -2) === '?>') { + $this->output .= "\n"; + } + } + + + + /** + * Expands macro and returns node & code. + * @param string + * @param string + * @param string + * @return MacroNode + */ + public function expandMacro($name, $args, $modifiers = NULL, HtmlNode $htmlNode = NULL, $prefix = NULL) + { + if (empty($this->macros[$name])) { + $cdata = $this->htmlNodes && in_array(strtolower(end($this->htmlNodes)->name), array('script', 'style')); + throw new CompileException("Unknown macro {{$name}}" . ($cdata ? " (in JavaScript or CSS, try to put a space after bracket.)" : '')); + } + foreach (array_reverse($this->macros[$name]) as $macro) { + $node = new MacroNode($macro, $name, $args, $modifiers, $this->macroNodes ? end($this->macroNodes) : NULL, $htmlNode, $prefix); + if ($macro->nodeOpened($node) !== FALSE) { + return $node; + } + } + throw new CompileException("Unhandled macro {{$name}}"); + } + +} diff --git a/libs/Nette/Latte/DefaultMacros.php b/libs/Nette/Latte/DefaultMacros.php deleted file mode 100644 index 834cb65..0000000 --- a/libs/Nette/Latte/DefaultMacros.php +++ /dev/null @@ -1,1100 +0,0 @@ - value} set template parameter - * - {default var => value} set default template parameter - * - {dump $var} - * - {debugbreak} - * - {l} {r} to display { } - * - * @author David Grudl - */ -class DefaultMacros extends Nette\Object -{ - /** @var array */ - public static $defaultMacros = array( - 'syntax' => '%:macroSyntax%', - '/syntax' => '%:macroSyntax%', - - 'block' => '', - '/block' => '', - - 'capture' => '', - '/capture' => '', - - 'snippet' => '', - '/snippet' => '', - - 'cache' => '', - '/cache' => 'tmp = array_pop($_l->g->caches); if (!$_l->tmp instanceof \stdClass) $_l->tmp->end(); } ?>', - - 'if' => '', - 'elseif' => '', - 'else' => '', - '/if' => '', - 'ifset' => '', - '/ifset' => '', - 'elseifset' => '', - 'foreach' => '', - '/foreach' => 'its); $iterator = end($_l->its) ?>', - 'for' => '', - '/for' => '', - 'while' => '', - '/while' => '', - 'continueIf' => '', - 'breakIf' => '', - 'first' => 'isFirst(%%)): ?>', - '/first' => '', - 'last' => 'isLast(%%)): ?>', - '/last' => '', - 'sep' => 'isLast(%%)): ?>', - '/sep' => '', - - 'include' => '', - 'extends' => '', - 'layout' => '', - - 'plink' => '', - 'link' => '', - 'ifCurrent' => '', // deprecated; use n:class="$presenter->linkCurrent ? ..." - '/ifCurrent' => '', - 'widget' => '', - 'control' => '', - - '@href' => ' href=""', - '@class' => 'tmp = trim(implode(" ", array_unique(%:formatArray%)))) echo \' class="\' . %:escape%($_l->tmp) . \'"\' ?>', - '@attr' => 'tmp = (string) (%%)) !== \'\') echo \' @@="\' . %:escape%($_l->tmp) . \'"\' ?>', - - 'attr' => '%:macroAttr%attributes() ?>', - 'contentType' => '', - 'status' => 'setCode(%%) ?>', - 'var' => '', - 'assign' => '', // deprecated - 'default' => '', - 'dump' => '', - 'debugbreak' => '', - 'l' => '{', - 'r' => '}', - - '_' => '', - '=' => '', - '?' => '', - ); - - /** @internal PHP identifier */ - const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF]*'; - - /** @internal */ - const T_WHITESPACE = T_WHITESPACE, - T_COMMENT = T_COMMENT, - T_SYMBOL = -1, - T_NUMBER = -2, - T_VARIABLE = -3; - - /** @var Nette\Utils\Tokenizer */ - private $tokenizer; - - /** @var Parser */ - private $parser; - - /** @var array */ - private $blocks = array(); - - /** @var array */ - private $namedBlocks = array(); - - /** @var bool */ - private $extends; - - /** @var string */ - private $uniq; - - /** @var int */ - private $cacheCounter; - - /** @internal block type */ - const BLOCK_NAMED = 1, - BLOCK_CAPTURE = 2, - BLOCK_ANONYMOUS = 3; - - - - /** - * Constructor. - */ - public function __construct() - { - $this->tokenizer = new Tokenizer(array( - self::T_WHITESPACE => '\s+', - self::T_COMMENT => '(?s)/\*.*?\*/', - Parser::RE_STRING, - '(?:true|false|null|and|or|xor|clone|new|instanceof|return|continue|break|[A-Z_][A-Z0-9_]{2,})(?![\d\pL_])', // keyword or const - '\([a-z]+\)', // type casting - self::T_VARIABLE => '\$[\d\pL_]+', - self::T_NUMBER => '[+-]?[0-9]+(?:\.[0-9]+)?(?:e[0-9]+)?', - self::T_SYMBOL => '[\d\pL_]+(?:-[\d\pL_]+)*', - '::|=>|[^"\']', // =>, any char except quotes - ), 'u'); - } - - - - /** - * Initializes parsing. - * @param Parser - * @return void - */ - public function initialize($parser) - { - $this->parser = $parser; - $this->blocks = array(); - $this->namedBlocks = array(); - $this->extends = NULL; - $this->uniq = Strings::random(); - $this->cacheCounter = 0; - } - - - - /** - * Finishes parsing. - * @param string - * @return void - */ - public function finalize(& $s) - { - // blocks closing check - if (count($this->blocks) === 1) { // auto-close last block - $s .= $this->parser->macro('/block'); - } - - // extends support - if ($this->namedBlocks || $this->extends) { - $s = 'extends) { - ob_start(); -} elseif (isset($presenter, $control) && $presenter->isAjax() && $control->isControlInvalid()) { - return Nette\Latte\DefaultMacros::renderSnippets($control, $_l, get_defined_vars()); -} -?>' . $s . 'extends) { - ob_end_clean(); - Nette\Latte\DefaultMacros::includeTemplate($_l->extends, get_defined_vars(), $template)->render(); -} -'; - } else { - $s = 'isAjax() && $control->isControlInvalid()) { - return Nette\Latte\DefaultMacros::renderSnippets($control, $_l, get_defined_vars()); -} -?>' . $s; - } - - // named blocks - if ($this->namedBlocks) { - $uniq = $this->uniq; - foreach (array_reverse($this->namedBlocks, TRUE) as $name => $foo) { - $code = & $this->namedBlocks[$name]; - $namere = preg_quote($name, '#'); - $s = Strings::replace($s, - "#{block $namere} \?>(.*)<\?php {/block $namere}#sU", - function ($matches) use ($name, & $code, $uniq) { - list(, $content) = $matches; - $func = '_lb' . substr(md5($uniq . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name); - $code = "//\n// block $name\n//\n" - . "if (!function_exists(\$_l->blocks[" . var_export($name, TRUE) . "][] = '$func')) { " - . "function $func(\$_l, \$_args) { " - . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v') // PHP bug #46873 - . ($name[0] === '_' ? '; $control->validateControl(' . var_export(substr($name, 1), TRUE) . ')' : '') // snippet - . "\n?>$contentnamedBlocks) . "\n\n//\n// end of blocks\n//\n?>" . $s; - } - - // internal state holder - $s = "extends, TRUE) . ', ' . var_export($this->uniq, TRUE) . '); unset($_extends);' - . "\n?>" . $s; - } - - - - /********************* macros ****************d*g**/ - - - - /** - * {_$var |modifiers} - */ - public function macroTranslate($var, $modifiers) - { - return $this->formatModifiers($this->formatMacroArgs($var), '|translate' . $modifiers); - } - - - - /** - * {syntax ...} - */ - public function macroSyntax($var) - { - switch ($var) { - case '': - case 'latte': - $this->parser->setDelimiters('\\{(?![\\s\'"{}])', '\\}'); // {...} - break; - - case 'double': - $this->parser->setDelimiters('\\{\\{(?![\\s\'"{}])', '\\}\\}'); // {{...}} - break; - - case 'asp': - $this->parser->setDelimiters('<%\s*', '\s*%>'); /* <%...%> */ - break; - - case 'python': - $this->parser->setDelimiters('\\{[{%]\s*', '\s*[%}]\\}'); // {% ... %} | {{ ... }} - break; - - case 'off': - $this->parser->setDelimiters('[^\x00-\xFF]', ''); - break; - - default: - throw new ParseException("Unknown syntax '$var'", 0, $this->parser->line); - } - } - - - - /** - * {include ...} - */ - public function macroInclude($content, $modifiers, $isDefinition = FALSE) - { - $destination = $this->fetchToken($content); // destination [,] [params] - $params = $this->formatArray($content) . ($content ? ' + ' : ''); - - if ($destination === NULL) { - throw new ParseException("Missing destination in {include}", 0, $this->parser->line); - - } elseif ($destination[0] === '#') { // include #block - $destination = ltrim($destination, '#'); - if (!Strings::match($destination, '#^\$?' . self::RE_IDENTIFIER . '$#')) { - throw new ParseException("Included block name must be alphanumeric string, '$destination' given.", 0, $this->parser->line); - } - - $parent = $destination === 'parent'; - if ($destination === 'parent' || $destination === 'this') { - $item = end($this->blocks); - while ($item && $item[0] !== self::BLOCK_NAMED) $item = prev($this->blocks); - if (!$item) { - throw new ParseException("Cannot include $destination block outside of any block.", 0, $this->parser->line); - } - $destination = $item[1]; - } - $name = $destination[0] === '$' ? $destination : var_export($destination, TRUE); - $params .= $isDefinition ? 'get_defined_vars()' : '$template->getParams()'; - $cmd = isset($this->namedBlocks[$destination]) && !$parent - ? "call_user_func(reset(\$_l->blocks[$name]), \$_l, $params)" - : 'Nette\Latte\DefaultMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_l, $name, $params)"; - return $modifiers - ? "ob_start(); $cmd; echo " . $this->formatModifiers('ob_get_clean()', $modifiers) - : $cmd; - - } else { // include "file" - $destination = $this->formatString($destination); - $cmd = 'Nette\Latte\DefaultMacros::includeTemplate(' . $destination . ', ' - . $params . '$template->getParams(), $_l->templates[' . var_export($this->uniq, TRUE) . '])'; - return $modifiers - ? 'echo ' . $this->formatModifiers($cmd . '->__toString(TRUE)', $modifiers) - : $cmd . '->render()'; - } - } - - - - /** - * {extends ...} - */ - public function macroExtends($content) - { - if (!$content) { - throw new ParseException("Missing destination in {extends}", 0, $this->parser->line); - } - if (!empty($this->blocks)) { - throw new ParseException("{extends} must be placed outside any block.", 0, $this->parser->line); - } - if ($this->extends !== NULL) { - throw new ParseException("Multiple {extends} declarations are not allowed.", 0, $this->parser->line); - } - $this->extends = $content !== 'none'; - return $this->extends ? '$_l->extends = ' . ($content === 'auto' ? '$layout' : $this->formatMacroArgs($content)) : ''; - } - - - - /** - * {block ...} - */ - public function macroBlock($content, $modifiers) - { - $name = $this->fetchToken($content); // block [,] [params] - - if ($name === NULL) { // anonymous block - $this->blocks[] = array(self::BLOCK_ANONYMOUS, NULL, $modifiers); - return $modifiers === '' ? '' : 'ob_start()'; - - } else { // #block - $name = ltrim($name, '#'); - if (!Strings::match($name, '#^' . self::RE_IDENTIFIER . '$#')) { - throw new ParseException("Block name must be alphanumeric string, '$name' given.", 0, $this->parser->line); - - } elseif (isset($this->namedBlocks[$name])) { - throw new ParseException("Cannot redeclare block '$name'", 0, $this->parser->line); - } - - $top = empty($this->blocks); - $this->namedBlocks[$name] = $name; - $this->blocks[] = array(self::BLOCK_NAMED, $name, ''); - if ($name[0] === '_') { // snippet - $tag = $this->fetchToken($content); // [name [,]] [tag] - $tag = trim($tag, '<>'); - $namePhp = var_export(substr($name, 1), TRUE); - $tag = $tag ? $tag : 'div'; - return "?><$tag id=\"getSnippetId($namePhp) ?>\">macroInclude('#' . $name, $modifiers) - . " ?>macroInclude('#' . $name, $modifiers, TRUE) . "{block $name}"; - - } elseif ($this->extends) { - return "{block $name}"; - - } else { - return 'if (!$_l->extends) { ' . $this->macroInclude('#' . $name, $modifiers, TRUE) . "; } {block $name}"; - } - } - } - - - - /** - * {/block} - */ - public function macroBlockEnd($content) - { - list($type, $name, $modifiers) = array_pop($this->blocks); - - if ($type === self::BLOCK_CAPTURE) { // capture - back compatibility - $this->blocks[] = array($type, $name, $modifiers); - return $this->macroCaptureEnd($content); - - } elseif ($type === self::BLOCK_NAMED) { // block - return "{/block $name}"; - - } else { // anonymous block - return $modifiers === '' ? '' : 'echo ' . $this->formatModifiers('ob_get_clean()', $modifiers); - } - } - - - - /** - * {snippet ...} - */ - public function macroSnippet($content) - { - return $this->macroBlock('_' . $content, ''); - } - - - - /** - * {snippet ...} - */ - public function macroSnippetEnd($content) - { - return $this->macroBlockEnd('', ''); - } - - - - /** - * {capture ...} - */ - public function macroCapture($content, $modifiers) - { - $name = $this->fetchToken($content); // $variable - - if (substr($name, 0, 1) !== '$') { - throw new ParseException("Invalid capture block parameter '$name'", 0, $this->parser->line); - } - - $this->blocks[] = array(self::BLOCK_CAPTURE, $name, $modifiers); - return 'ob_start()'; - } - - - - /** - * {/capture} - */ - public function macroCaptureEnd($content) - { - list($type, $name, $modifiers) = array_pop($this->blocks); - return $name . '=' . $this->formatModifiers('ob_get_clean()', $modifiers); - } - - - - /** - * {cache ...} - */ - public function macroCache($content) - { - return 'if (Nette\Latte\DefaultMacros::createCache($netteCacheStorage, ' - . var_export($this->uniq . ':' . $this->cacheCounter++, TRUE) - . ', $_l->g->caches' . $this->formatArray($content, ', ') . ')) {'; - } - - - - /** - * {foreach ...} - */ - public function macroForeach($content) - { - return '$iterator = $_l->its[] = new Nette\Iterators\CachingIterator(' - . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $this->formatMacroArgs($content), 1); - } - - - - /** - * {ifset ...} - */ - public function macroIfset($content) - { - if (strpos($content, '#') === FALSE) { - return $content; - } - $list = array(); - while (($name = $this->fetchToken($content)) !== NULL) { - $list[] = $name[0] === '#' ? '$_l->blocks["' . substr($name, 1) . '"]' : $name; - } - return implode(', ', $list); - } - - - - /** - * {attr ...} - */ - public function macroAttr($content) - { - return Strings::replace($content . ' ', '#\)\s+#', ')->'); - } - - - - /** - * {contentType ...} - */ - public function macroContentType($content) - { - if (strpos($content, 'html') !== FALSE) { - $this->parser->escape = 'Nette\Templating\DefaultHelpers::escapeHtml|'; - $this->parser->context = Parser::CONTEXT_TEXT; - - } elseif (strpos($content, 'xml') !== FALSE) { - $this->parser->escape = 'Nette\Templating\DefaultHelpers::escapeXml'; - $this->parser->context = Parser::CONTEXT_NONE; - - } elseif (strpos($content, 'javascript') !== FALSE) { - $this->parser->escape = 'Nette\Templating\DefaultHelpers::escapeJs'; - $this->parser->context = Parser::CONTEXT_NONE; - - } elseif (strpos($content, 'css') !== FALSE) { - $this->parser->escape = 'Nette\Templating\DefaultHelpers::escapeCss'; - $this->parser->context = Parser::CONTEXT_NONE; - - } elseif (strpos($content, 'plain') !== FALSE) { - $this->parser->escape = ''; - $this->parser->context = Parser::CONTEXT_NONE; - - } else { - $this->parser->escape = '$template->escape'; - $this->parser->context = Parser::CONTEXT_NONE; - } - - // temporary solution - if (strpos($content, '/')) { - return '$netteHttpResponse->setHeader("Content-Type", "' . $content . '")'; - } - } - - - - /** - * {dump ...} - */ - public function macroDump($content) - { - return 'Nette\Diagnostics\Debugger::barDump(' - . ($content ? 'array(' . var_export($this->formatMacroArgs($content), TRUE) . " => $content)" : 'get_defined_vars()') - . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))'; - } - - - - /** - * {debugbreak} - */ - public function macroDebugbreak() - { - return 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()'; - } - - - - /** - * {control ...} - */ - public function macroControl($content) - { - $pair = $this->fetchToken($content); // control[:method] - if ($pair === NULL) { - throw new ParseException("Missing control name in {control}", 0, $this->parser->line); - } - $pair = explode(':', $pair, 2); - $name = $this->formatString($pair[0]); - $method = isset($pair[1]) ? ucfirst($pair[1]) : ''; - $method = Strings::match($method, '#^(' . self::RE_IDENTIFIER . '|)$#') ? "render$method" : "{\"render$method\"}"; - $param = $this->formatArray($content); - if (strpos($content, '=>') === FALSE) { - $param = substr($param, 6, -1); // removes array() - } - return ($name[0] === '$' ? "if (is_object($name)) \$_ctrl = $name; else " : '') - . '$_ctrl = $control->getWidget(' . $name . '); ' - . 'if ($_ctrl instanceof Nette\Application\UI\IPartiallyRenderable) $_ctrl->validateControl(); ' - . "\$_ctrl->$method($param)"; - } - - - - /** - * {link ...} - */ - public function macroLink($content, $modifiers) - { - return $this->formatModifiers('$control->link(' . $this->formatLink($content) .')', $modifiers); - } - - - - /** - * {plink ...} - */ - public function macroPlink($content, $modifiers) - { - return $this->formatModifiers('$presenter->link(' . $this->formatLink($content) .')', $modifiers); - } - - - - /** - * {ifCurrent ...} - */ - public function macroIfCurrent($content) - { - return ($content ? 'try { $presenter->link(' . $this->formatLink($content) . '); } catch (Nette\Application\UI\InvalidLinkException $e) {}' : '') - . '; if ($presenter->getLastCreatedRequestFlag("current")):'; - } - - - - /** - * Formats {*link ...} parameters. - */ - private function formatLink($content) - { - return $this->formatString($this->fetchToken($content)) . $this->formatArray($content, ', '); // destination [,] args - } - - - - /** - * {var ...} - */ - public function macroVar($content, $modifiers, $extract = FALSE) - { - $out = ''; - $var = TRUE; - foreach ($this->parseMacroArgs($content) as $token) { - if ($var && ($token['type'] === self::T_SYMBOL || $token['type'] === self::T_VARIABLE)) { - if ($extract) { - $out .= "'" . trim($token['value'], "'$") . "'"; - } else { - $out .= '$' . trim($token['value'], "'$"); - } - } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) { - $out .= $extract ? '=>' : '='; - $var = FALSE; - - } elseif ($token['value'] === ',' && $token['depth'] === 0) { - $out .= $extract ? ',' : ';'; - $var = TRUE; - } else { - $out .= $token['value']; - } - } - return $out; - } - - - - /** - * {default ...} - */ - public function macroDefault($content) - { - return 'extract(array(' . $this->macroVar($content, '', TRUE) . '), EXTR_SKIP)'; - } - - - - /** - * Just modifiers helper. - */ - public function macroModifiers($content, $modifiers) - { - return $this->formatModifiers($this->formatMacroArgs($content), $modifiers); - } - - - - /** - * Escaping helper. - */ - public function escape() - { - $tmp = explode('|', $this->parser->escape); - return $tmp[0]; - } - - - - /********************* compile-time helpers ****************d*g**/ - - - - /** - * Applies modifiers. - * @param string - * @param string - * @return string - */ - public function formatModifiers($var, $modifiers) - { - if (!$modifiers) { - return $var; - } - $inside = FALSE; - foreach ($this->parseMacroArgs(ltrim($modifiers, '|')) as $token) { - if ($token['type'] === self::T_WHITESPACE) { - $var = rtrim($var) . ' '; - - } elseif (!$inside) { - if ($token['type'] === self::T_SYMBOL) { - if (trim($token['value'], "'") === 'escape') { - $tmp = explode('|', $this->parser->escape); - $var = $tmp[0] . "($var" . (isset($tmp[1]) ? ', ' . var_export($tmp[1], TRUE) : ''); - } else { - $var = "\$template->" . trim($token['value'], "'") . "($var"; - } - $inside = TRUE; - } else { - throw new ParseException("Modifier name must be alphanumeric string, '$token[value]' given.", 0, $this->parser->line); - } - } else { - if ($token['value'] === ':' || $token['value'] === ',') { - $var = $var . ', '; - - } elseif ($token['value'] === '|') { - $var = $var . ')'; - $inside = FALSE; - - } else { - $var .= $token['value']; - } - } - } - return $inside ? "$var)" : $var; - } - - - - /** - * Reads single token (optionally delimited by comma) from string. - * @param string - * @return string - */ - public function fetchToken(& $s) - { - if ($matches = Strings::match($s, '#^((?>'.Parser::RE_STRING.'|[^\'"\s,]+)+)\s*,?\s*(.*)$#s')) { // token [,] tail - $s = $matches[2]; - return $matches[1]; - } - return NULL; - } - - - - /** - * Reformats Latte to PHP code. - * @param string - * @param string - * @return string - */ - public function formatMacroArgs($input) - { - $out = ''; - foreach ($this->parseMacroArgs($input) as $token) { - $out .= $token['value']; - } - return $out; - } - - - - /** - * Reformats Latte to PHP array. - * @param string - * @param string - * @return string - */ - public function formatArray($input, $prefix = '') - { - $tokens = $this->parseMacroArgs($input); - if (!$tokens) { - return ''; - } - $out = ''; - $expand = NULL; - $tokens[] = NULL; // sentinel - foreach ($tokens as $token) { - if ($token['value'] === '(expand)' && $token['depth'] === 0) { - $expand = TRUE; - $out .= '),'; - - } elseif ($expand && ($token['value'] === ',' || $token['value'] === NULL) && !$token['depth']) { - $expand = FALSE; - $out .= ', array('; - } else { - $out .= $token['value']; - } - } - return $prefix . ($expand === NULL ? "array($out)" : "array_merge(array($out))"); - } - - - - /** - * Formats parameter to PHP string. - * @param string - * @return string - */ - public function formatString($s) - { - static $keywords = array('true'=>1, 'false'=>1, 'null'=>1); - return (is_numeric($s) || strspn($s, '\'"$') || isset($keywords[strtolower($s)])) ? $s : '"' . $s . '"'; - } - - - - /** - * Tokenizer and preparser. - * @return array - */ - private function parseMacroArgs($input) - { - $this->tokenizer->tokenize($input); - - $inTernary = $lastSymbol = $prev = NULL; - $tokens = $arrays = array(); - $n = -1; - while (++$n < count($this->tokenizer->tokens)) { - $token = $this->tokenizer->tokens[$n]; - $token['depth'] = $depth = count($arrays); - - if ($token['type'] === self::T_COMMENT) { - continue; // remove comments - - } elseif ($token['type'] === self::T_WHITESPACE) { - $tokens[] = $token; - continue; - - } elseif ($token['type'] === self::T_SYMBOL && ($prev === NULL || in_array($prev['value'], array(',', '(', '[', '=', '=>', ':', '?')))) { - $lastSymbol = count($tokens); // quoting pre-requirements - - } elseif (is_int($lastSymbol) && in_array($token['value'], array(',', ')', ']', '=', '=>', ':', '|'))) { - $tokens[$lastSymbol]['value'] = "'" . $tokens[$lastSymbol]['value'] . "'"; // quote symbols - $lastSymbol = NULL; - - } else { - $lastSymbol = NULL; - } - - if ($token['value'] === '?') { // short ternary operators without : - $inTernary = $depth; - - } elseif ($token['value'] === ':') { - $inTernary = NULL; - - } elseif ($inTernary === $depth && ($token['value'] === ',' || $token['value'] === ')' || $token['value'] === ']')) { // close ternary - $tokens[] = Tokenizer::createToken(':') + array('depth' => $depth); - $tokens[] = Tokenizer::createToken('null') + array('depth' => $depth); - $inTernary = NULL; - } - - if ($token['value'] === '[') { // simplified array syntax [...] - if ($arrays[] = $prev['value'] !== ']' && $prev['type'] !== self::T_SYMBOL && $prev['type'] !== self::T_VARIABLE) { - $tokens[] = Tokenizer::createToken('array') + array('depth' => $depth); - $token = Tokenizer::createToken('('); - } - } elseif ($token['value'] === ']') { - if (array_pop($arrays) === TRUE) { - $token = Tokenizer::createToken(')'); - } - } elseif ($token['value'] === '(') { // only count - $arrays[] = '('; - - } elseif ($token['value'] === ')') { // only count - array_pop($arrays); - } - - $tokens[] = $prev = $token; - } - - if (is_int($lastSymbol)) { - $tokens[$lastSymbol]['value'] = "'" . $tokens[$lastSymbol]['value'] . "'"; // quote symbols - } - if ($inTernary !== NULL) { // close ternary - $tokens[] = Tokenizer::createToken(':') + array('depth' => count($arrays)); - $tokens[] = Tokenizer::createToken('null') + array('depth' => count($arrays)); - } - - return $tokens; - } - - - - /********************* run-time helpers ****************d*g**/ - - - - /** - * Calls block. - * @param stdClass - * @param string - * @param array - * @return void - */ - public static function callBlock($context, $name, $params) - { - if (empty($context->blocks[$name])) { - throw new Nette\InvalidStateException("Cannot include undefined block '$name'."); - } - $block = reset($context->blocks[$name]); - $block($context, $params); - } - - - - /** - * Calls parent block. - * @param stdClass - * @param string - * @param array - * @return void - */ - public static function callBlockParent($context, $name, $params) - { - if (empty($context->blocks[$name]) || ($block = next($context->blocks[$name])) === FALSE) { - throw new Nette\InvalidStateException("Cannot include undefined parent block '$name'."); - } - $block($context, $params); - } - - - - /** - * Includes subtemplate. - * @param mixed included file name or template - * @param array parameters - * @param Nette\Templating\ITemplate current template - * @return Nette\Templating\Template - */ - public static function includeTemplate($destination, $params, $template) - { - if ($destination instanceof Nette\Templating\ITemplate) { - $tpl = $destination; - - } elseif ($destination == NULL) { // intentionally == - throw new Nette\InvalidArgumentException("Template file name was not specified."); - - } else { - $tpl = clone $template; - if ($template instanceof Nette\Templating\IFileTemplate) { - if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') { - $destination = dirname($template->getFile()) . '/' . $destination; - } - $tpl->setFile($destination); - } - } - - $tpl->setParams($params); // interface? - return $tpl; - } - - - - /** - * Initializes local & global storage in template. - * @param Nette\Templating\ITemplate - * @param bool - * @param string - * @return stdClass - */ - public static function initRuntime($template, $extends, $realFile) - { - $local = (object) NULL; - - // extends support - if (isset($template->_l)) { - $local->blocks = & $template->_l->blocks; - $local->templates = & $template->_l->templates; - } - $local->templates[$realFile] = $template; - $local->extends = is_bool($extends) ? $extends : (empty($template->_extends) ? FALSE : $template->_extends); - unset($template->_l, $template->_extends); - - // global storage - if (!isset($template->_g)) { - $template->_g = (object) NULL; - } - $local->g = $template->_g; - - // cache support - if (!empty($local->g->caches)) { - end($local->g->caches)->dependencies[Nette\Caching\Cache::FILES][] = $template->getFile(); - } - - return $local; - } - - - - public static function renderSnippets($control, $local, $params) - { - $payload = $control->getPresenter()->getPayload(); - if (isset($local->blocks)) { - foreach ($local->blocks as $name => $function) { - if ($name[0] !== '_' || !$control->isControlInvalid(substr($name, 1))) { - continue; - } - ob_start(); - $function = reset($function); - $function($local, $params); - $payload->snippets[$control->getSnippetId(substr($name, 1))] = ob_get_clean(); - } - } - if ($control instanceof Nette\Application\UI\Control) { - foreach ($control->getComponents(FALSE, 'Nette\Application\UI\Control') as $child) { - if ($child->isControlInvalid()) { - $child->render(); - } - } - } - } - - - - /** - * Starts the output cache. Returns Nette\Caching\OutputHelper object if buffering was started. - * @param Nette\Caching\IStorage - * @param string - * @param array of Nette\Caching\OutputHelper - * @param array - * @return Nette\Caching\OutputHelper - */ - public static function createCache(Nette\Caching\IStorage $cacheStorage, $key, & $parents, $args = NULL) - { - if ($args) { - if (array_key_exists('if', $args) && !$args['if']) { - return $parents[] = (object) NULL; - } - $key = array_merge(array($key), array_intersect_key($args, range(0, count($args)))); - } - if ($parents) { - end($parents)->dependencies[Nette\Caching\Cache::ITEMS][] = $key; - } - - $cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Templating.Cache'); - if ($helper = $cache->start($key)) { - $helper->dependencies = array( - Nette\Caching\Cache::TAGS => isset($args['tags']) ? $args['tags'] : NULL, - Nette\Caching\Cache::EXPIRATION => isset($args['expire']) ? $args['expire'] : '+ 7 days', - ); - $parents[] = $helper; - } - return $helper; - } - -} diff --git a/libs/Nette/Latte/Engine.php b/libs/Nette/Latte/Engine.php index 29e9a0e..468c9cd 100644 --- a/libs/Nette/Latte/Engine.php +++ b/libs/Nette/Latte/Engine.php @@ -1,52 +1,77 @@ -parser = new Parser; - $this->parser->handler = new DefaultMacros; - $this->parser->macros = DefaultMacros::$defaultMacros; - } - - - - /** - * Invokes filter. - * @param string - * @return string - */ - public function __invoke($s) - { - $this->parser->context = Parser::CONTEXT_TEXT; - $this->parser->escape = 'Nette\Templating\DefaultHelpers::escapeHtml|'; - $this->parser->setDelimiters('\\{(?![\\s\'"{}*])', '\\}'); - return $this->parser->parse($s); - } - -} +parser = new Parser; + $this->compiler = new Compiler; + $this->compiler->defaultContentType = Compiler::CONTENT_XHTML; + + Macros\CoreMacros::install($this->compiler); + $this->compiler->addMacro('cache', new Macros\CacheMacro($this->compiler)); + Macros\UIMacros::install($this->compiler); + Macros\FormMacros::install($this->compiler); + } + + + + /** + * Invokes filter. + * @param string + * @return string + */ + public function __invoke($s) + { + return $this->compiler->compile($this->parser->parse($s)); + } + + + + /** + * @return Parser + */ + public function getParser() + { + return $this->parser; + } + + + + /** + * @return Compiler + */ + public function getCompiler() + { + return $this->compiler; + } + +} diff --git a/libs/Nette/Latte/HtmlNode.php b/libs/Nette/Latte/HtmlNode.php index ef7890b..b4e5858 100644 --- a/libs/Nette/Latte/HtmlNode.php +++ b/libs/Nette/Latte/HtmlNode.php @@ -1,49 +1,53 @@ -name = $name; - $this->isEmpty = isset(Nette\Utils\Html::$emptyElements[strtolower($this->name)]); - } - -} +name = $name; + } + +} diff --git a/libs/Nette/Latte/IMacro.php b/libs/Nette/Latte/IMacro.php new file mode 100644 index 0000000..55917b7 --- /dev/null +++ b/libs/Nette/Latte/IMacro.php @@ -0,0 +1,50 @@ +name = $name; - $this->args = $args; - $this->modifiers = $modifiers; - } - -} +macro = $macro; + $this->name = (string) $name; + $this->modifiers = (string) $modifiers; + $this->parentNode = $parentNode; + $this->htmlNode = $htmlNode; + $this->prefix = $prefix; + $this->tokenizer = new MacroTokenizer($this->args); + $this->data = new \stdClass; + $this->setArgs($args); + } + + + + public function setArgs($args) + { + $this->args = (string) $args; + $this->tokenizer->tokenize($this->args); + } + +} diff --git a/libs/Nette/Latte/MacroTokenizer.php b/libs/Nette/Latte/MacroTokenizer.php new file mode 100644 index 0000000..d1fb7bf --- /dev/null +++ b/libs/Nette/Latte/MacroTokenizer.php @@ -0,0 +1,69 @@ + '\s+', + self::T_COMMENT => '(?s)/\*.*?\*/', + self::T_STRING => Parser::RE_STRING, + self::T_KEYWORD => '(?:true|false|null|and|or|xor|clone|new|instanceof|return|continue|break|[A-Z_][A-Z0-9_]{2,})(?![\w\pL_])', // keyword or const + self::T_CAST => '\((?:expand|string|array|int|integer|float|bool|boolean|object)\)', // type casting + self::T_VARIABLE => '\$[\w\pL_]+', + self::T_NUMBER => '[+-]?[0-9]+(?:\.[0-9]+)?(?:e[0-9]+)?', + self::T_SYMBOL => '[\w\pL_]+(?:-[\w\pL_]+)*', + self::T_CHAR => '::|=>|[^"\']', // =>, any char except quotes + ), 'u'); + $this->ignored = array(self::T_COMMENT, self::T_WHITESPACE); + $this->tokenize($input); + } + + + + /** + * Reads single token (optionally delimited by comma) from string. + * @param string + * @return string + */ + public function fetchWord() + { + $word = $this->fetchUntil(self::T_WHITESPACE, ','); + $this->fetch(','); + $this->fetchAll(self::T_WHITESPACE, self::T_COMMENT); + return $word; + } + +} diff --git a/libs/Nette/Latte/Macros/CacheMacro.php b/libs/Nette/Latte/Macros/CacheMacro.php new file mode 100644 index 0000000..9da7142 --- /dev/null +++ b/libs/Nette/Latte/Macros/CacheMacro.php @@ -0,0 +1,132 @@ +used = FALSE; + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + if ($this->used) { + return array('Nette\Latte\Macros\CacheMacro::initRuntime($template, $_g);'); + } + } + + + + /** + * New node is found. + * @return bool + */ + public function nodeOpened(Latte\MacroNode $node) + { + $this->used = TRUE; + $node->isEmpty = FALSE; + $node->openingCode = Latte\PhpWriter::using($node) + ->write('caches, %node.array?)) { ?>', + Nette\Utils\Strings::random() + ); + } + + + + /** + * Node is closed. + * @return void + */ + public function nodeClosed(Latte\MacroNode $node) + { + $node->closingCode = 'tmp = array_pop($_g->caches); if (!$_l->tmp instanceof stdClass) $_l->tmp->end(); } ?>'; + } + + + + /********************* run-time helpers ****************d*g**/ + + + + /** + * @return void + */ + public static function initRuntime(Nette\Templating\FileTemplate $template, \stdClass $global) + { + if (!empty($global->caches)) { + end($global->caches)->dependencies[Nette\Caching\Cache::FILES][] = $template->getFile(); + } + } + + + + /** + * Starts the output cache. Returns Nette\Caching\OutputHelper object if buffering was started. + * @param Nette\Caching\IStorage + * @param string + * @param Nette\Caching\OutputHelper[] + * @param array + * @return Nette\Caching\OutputHelper + */ + public static function createCache(Nette\Caching\IStorage $cacheStorage, $key, & $parents, array $args = NULL) + { + if ($args) { + if (array_key_exists('if', $args) && !$args['if']) { + return $parents[] = (object) NULL; + } + $key = array_merge(array($key), array_intersect_key($args, range(0, count($args)))); + } + if ($parents) { + end($parents)->dependencies[Nette\Caching\Cache::ITEMS][] = $key; + } + + $cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Templating.Cache'); + if ($helper = $cache->start($key)) { + if (isset($args['expire'])) { + $args['expiration'] = $args['expire']; // back compatibility + } + $helper->dependencies = array( + Nette\Caching\Cache::TAGS => isset($args['tags']) ? $args['tags'] : NULL, + Nette\Caching\Cache::EXPIRATION => isset($args['expiration']) ? $args['expiration'] : '+ 7 days', + ); + $parents[] = $helper; + } + return $helper; + } + +} diff --git a/libs/Nette/Latte/Macros/CoreMacros.php b/libs/Nette/Latte/Macros/CoreMacros.php new file mode 100644 index 0000000..0d31aa7 --- /dev/null +++ b/libs/Nette/Latte/Macros/CoreMacros.php @@ -0,0 +1,413 @@ + value} set template parameter + * - {default var => value} set default template parameter + * - {dump $var} + * - {debugbreak} + * - {l} {r} to display { } + * + * @author David Grudl + */ +class CoreMacros extends MacroSet +{ + + + public static function install(Latte\Compiler $compiler) + { + $me = new static($compiler); + + $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf')); + $me->addMacro('elseif', 'elseif (%node.args):'); + $me->addMacro('else', array($me, 'macroElse')); + $me->addMacro('ifset', 'if (isset(%node.args)):', 'endif'); + $me->addMacro('elseifset', 'elseif (isset(%node.args)):'); + + $me->addMacro('foreach', '', array($me, 'macroEndForeach')); + $me->addMacro('for', 'for (%node.args):', 'endfor'); + $me->addMacro('while', 'while (%node.args):', 'endwhile'); + $me->addMacro('continueIf', 'if (%node.args) continue'); + $me->addMacro('breakIf', 'if (%node.args) break'); + $me->addMacro('first', 'if ($iterator->isFirst(%node.args)):', 'endif'); + $me->addMacro('last', 'if ($iterator->isLast(%node.args)):', 'endif'); + $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)):', 'endif'); + + $me->addMacro('var', array($me, 'macroVar')); + $me->addMacro('assign', array($me, 'macroVar')); // deprecated + $me->addMacro('default', array($me, 'macroVar')); + $me->addMacro('dump', array($me, 'macroDump')); + $me->addMacro('debugbreak', array($me, 'macroDebugbreak')); + $me->addMacro('l', '?>{addMacro('r', '?>}addMacro('_', array($me, 'macroTranslate'), array($me, 'macroTranslate')); + $me->addMacro('=', array($me, 'macroExpr')); + $me->addMacro('?', array($me, 'macroExpr')); + + $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd')); + $me->addMacro('include', array($me, 'macroInclude')); + $me->addMacro('use', array($me, 'macroUse')); + + $me->addMacro('class', NULL, NULL, array($me, 'macroClass')); + $me->addMacro('attr', array($me, 'macroOldAttr'), '', array($me, 'macroAttr')); + $me->addMacro('href', NULL); // TODO: placeholder + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, ' + . var_export($this->getCompiler()->getTemplateId(), TRUE) . ')'); + } + + + + /********************* macros ****************d*g**/ + + + + /** + * {if ...} + */ + public function macroIf(MacroNode $node, PhpWriter $writer) + { + if ($node->data->capture = ($node->args === '')) { + return 'ob_start()'; + } + if ($node->prefix === $node::PREFIX_TAG) { + return $writer->write($node->htmlNode->closing ? 'if (array_pop($_l->ifs)):' : 'if ($_l->ifs[] = (%node.args)):'); + } + return $writer->write('if (%node.args):'); + } + + + + /** + * {/if ...} + */ + public function macroEndIf(MacroNode $node, PhpWriter $writer) + { + if ($node->data->capture) { + if ($node->args === '') { + throw new CompileException('Missing condition in {if} macro.'); + } + return $writer->write('if (%node.args) ' + . (isset($node->data->else) ? '{ ob_end_clean(); ob_end_flush(); }' : 'ob_end_flush();') + . ' else ' + . (isset($node->data->else) ? '{ $_else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_else; }' : 'ob_end_clean();') + ); + } + return 'endif'; + } + + + + /** + * {else} + */ + public function macroElse(MacroNode $node, PhpWriter $writer) + { + $ifNode = $node->parentNode; + if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) { + if (isset($ifNode->data->else)) { + throw new CompileException("Macro {if} supports only one {else}."); + } + $ifNode->data->else = TRUE; + return 'ob_start()'; + } + return 'else:'; + } + + + + /** + * {_$var |modifiers} + */ + public function macroTranslate(MacroNode $node, PhpWriter $writer) + { + if ($node->closing) { + return $writer->write('echo %modify($template->translate(ob_get_clean()))'); + + } elseif ($node->isEmpty = ($node->args !== '')) { + return $writer->write('echo %modify($template->translate(%node.args))'); + + } else { + return 'ob_start()'; + } + } + + + + /** + * {include "file" [,] [params]} + */ + public function macroInclude(MacroNode $node, PhpWriter $writer) + { + $code = $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParameters(), $_l->templates[%var])', + $this->getCompiler()->getTemplateId()); + + if ($node->modifiers) { + return $writer->write('echo %modify(%raw->__toString(TRUE))', $code); + } else { + return $code . '->render()'; + } + } + + + + /** + * {use class MacroSet} + */ + public function macroUse(MacroNode $node, PhpWriter $writer) + { + Nette\Callback::create($node->tokenizer->fetchWord(), 'install') + ->invoke($this->getCompiler()) + ->initialize(); + } + + + + /** + * {capture $variable} + */ + public function macroCapture(MacroNode $node, PhpWriter $writer) + { + $variable = $node->tokenizer->fetchWord(); + if (substr($variable, 0, 1) !== '$') { + throw new CompileException("Invalid capture block variable '$variable'"); + } + $node->data->variable = $variable; + return 'ob_start()'; + } + + + + /** + * {/capture} + */ + public function macroCaptureEnd(MacroNode $node, PhpWriter $writer) + { + return $node->data->variable . $writer->write(" = %modify(ob_get_clean())"); + } + + + + /** + * {foreach ...} + */ + public function macroEndForeach(MacroNode $node, PhpWriter $writer) + { + if (preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) { + $node->openingCode = 'its[] = new Nette\Iterators\CachingIterator(' + . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . '): ?>'; + $node->closingCode = 'its); $iterator = end($_l->its) ?>'; + } else { + $node->openingCode = 'formatArgs() . '): ?>'; + $node->closingCode = ''; + } + } + + + + /** + * n:class="..." + */ + public function macroClass(MacroNode $node, PhpWriter $writer) + { + return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\' . %escape(implode(" ", array_unique($_l->tmp))) . \'"\''); + } + + + + /** + * n:attr="..." + */ + public function macroAttr(MacroNode $node, PhpWriter $writer) + { + return $writer->write('echo Nette\Utils\Html::el(NULL, %node.array)->attributes()'); + } + + + + /** + * {attr ...} + * @deprecated + */ + public function macroOldAttr(MacroNode $node) + { + return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->'); + } + + + + /** + * {dump ...} + */ + public function macroDump(MacroNode $node, PhpWriter $writer) + { + $args = $writer->formatArgs(); + return 'Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(" . $writer->write('%var', $args) . " => $args)" : 'get_defined_vars()') + . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))'; + } + + + + /** + * {debugbreak ...} + */ + public function macroDebugbreak(MacroNode $node, PhpWriter $writer) + { + return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else') + . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()'); + } + + + + /** + * {var ...} + * {default ...} + */ + public function macroVar(MacroNode $node, PhpWriter $writer) + { + $out = ''; + $var = TRUE; + $tokenizer = $writer->preprocess(); + while ($token = $tokenizer->fetchToken()) { + if ($var && ($token['type'] === Latte\MacroTokenizer::T_SYMBOL || $token['type'] === Latte\MacroTokenizer::T_VARIABLE)) { + if ($node->name === 'default') { + $out .= "'" . ltrim($token['value'], "$") . "'"; + } else { + $out .= '$' . ltrim($token['value'], "$"); + } + $var = NULL; + + } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) { + $out .= $node->name === 'default' ? '=>' : '='; + $var = FALSE; + + } elseif ($token['value'] === ',' && $token['depth'] === 0) { + $out .= $node->name === 'default' ? ',' : ';'; + $var = TRUE; + + } elseif ($var === NULL && $node->name === 'default' && $token['type'] !== Latte\MacroTokenizer::T_WHITESPACE) { + throw new CompileException("Unexpected '$token[value]' in {default $node->args}"); + + } else { + $out .= $writer->canQuote($tokenizer) ? "'$token[value]'" : $token['value']; + } + } + return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out; + } + + + + /** + * {= ...} + * {? ...} + */ + public function macroExpr(MacroNode $node, PhpWriter $writer) + { + return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify(%node.args)'); + } + + + + /********************* run-time helpers ****************d*g**/ + + + + /** + * Includes subtemplate. + * @param mixed included file name or template + * @param array parameters + * @param Nette\Templating\ITemplate current template + * @return Nette\Templating\Template + */ + public static function includeTemplate($destination, array $params, Nette\Templating\ITemplate $template) + { + if ($destination instanceof Nette\Templating\ITemplate) { + $tpl = $destination; + + } elseif ($destination == NULL) { // intentionally == + throw new Nette\InvalidArgumentException("Template file name was not specified."); + + } elseif ($template instanceof Nette\Templating\IFileTemplate) { + if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') { + $destination = dirname($template->getFile()) . '/' . $destination; + } + $tpl = clone $template; + $tpl->setFile($destination); + + } else { + throw new Nette\NotSupportedException('Macro {include "filename"} is supported only with Nette\Templating\IFileTemplate.'); + } + + $tpl->setParameters($params); // interface? + return $tpl; + } + + + + /** + * Initializes local & global storage in template. + * @return \stdClass + */ + public static function initRuntime(Nette\Templating\ITemplate $template, $templateId) + { + // local storage + if (isset($template->_l)) { + $local = $template->_l; + unset($template->_l); + } else { + $local = (object) NULL; + } + $local->templates[$templateId] = $template; + + // global storage + if (!isset($template->_g)) { + $template->_g = (object) NULL; + } + + return array($local, $template->_g); + } + +} diff --git a/libs/Nette/Latte/Macros/FormMacros.php b/libs/Nette/Latte/Macros/FormMacros.php new file mode 100644 index 0000000..c2d26c4 --- /dev/null +++ b/libs/Nette/Latte/Macros/FormMacros.php @@ -0,0 +1,137 @@ +addMacro('form', + 'Nette\Latte\Macros\FormMacros::renderFormBegin($form = $_form = (is_object(%node.word) ? %node.word : $_control[%node.word]), %node.array)', + 'Nette\Latte\Macros\FormMacros::renderFormEnd($_form)'); + $me->addMacro('label', array($me, 'macroLabel'), '?>addMacro('input', 'echo $_form[%node.word]->getControl()->addAttributes(%node.array)', NULL, array($me, 'macroAttrInput')); + $me->addMacro('formContainer', '$_formStack[] = $_form; $formContainer = $_form = $_form[%node.word]', '$_form = array_pop($_formStack)'); + } + + + + /********************* macros ****************d*g**/ + + + /** + * {label ...} and optionally {/label} + */ + public function macroLabel(MacroNode $node, PhpWriter $writer) + { + $cmd = 'if ($_label = $_form[%node.word]->getLabel()) echo $_label->addAttributes(%node.array)'; + if ($node->isEmpty = (substr($node->args, -1) === '/')) { + $node->setArgs(substr($node->args, 0, -1)); + return $writer->write($cmd); + } else { + return $writer->write($cmd . '->startTag()'); + } + } + + + + /** + * n:input + */ + public function macroAttrInput(MacroNode $node, PhpWriter $writer) + { + if ($node->htmlNode->attrs) { + $reset = array_fill_keys(array_keys($node->htmlNode->attrs), NULL); + return $writer->write('echo $_form[%node.word]->getControl()->addAttributes(%var)->attributes()', $reset); + } + return $writer->write('echo $_form[%node.word]->getControl()->attributes()'); + } + + + + /********************* run-time writers ****************d*g**/ + + + + /** + * Renders form begin. + * @return void + */ + public static function renderFormBegin(Form $form, array $attrs) + { + $el = $form->getElementPrototype(); + $el->action = (string) $el->action; + $el = clone $el; + if (strcasecmp($form->getMethod(), 'get') === 0) { + list($el->action) = explode('?', $el->action, 2); + } + echo $el->addAttributes($attrs)->startTag(); + } + + + + /** + * Renders form end. + * @return string + */ + public static function renderFormEnd(Form $form) + { + $s = ''; + if (strcasecmp($form->getMethod(), 'get') === 0) { + $url = explode('?', $form->getElementPrototype()->action, 2); + if (isset($url[1])) { + foreach (preg_split('#[;&]#', $url[1]) as $param) { + $parts = explode('=', $param, 2); + $name = urldecode($parts[0]); + if (!isset($form[$name])) { + $s .= Nette\Utils\Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1]))); + } + } + } + } + + foreach ($form->getComponents(TRUE, 'Nette\Forms\Controls\HiddenField') as $control) { + if (!$control->getOption('rendered')) { + $s .= $control->getControl(); + } + } + + if (iterator_count($form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) { + $s .= ''; + } + + echo ($s ? "
    $s
    \n" : '') . $form->getElementPrototype()->endTag() . "\n"; + } + +} diff --git a/libs/Nette/Latte/Macros/MacroSet.php b/libs/Nette/Latte/Macros/MacroSet.php new file mode 100644 index 0000000..7b1d089 --- /dev/null +++ b/libs/Nette/Latte/Macros/MacroSet.php @@ -0,0 +1,143 @@ +compiler = $compiler; + } + + + + public function addMacro($name, $begin, $end = NULL, $attr = NULL) + { + $this->macros[$name] = array($begin, $end, $attr); + $this->compiler->addMacro($name, $this); + return $this; + } + + + + public static function install(Latte\Compiler $compiler) + { + return new static($compiler); + } + + + + /** + * Initializes before template parsing. + * @return void + */ + public function initialize() + { + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + } + + + + /** + * New node is found. + * @return bool + */ + public function nodeOpened(MacroNode $node) + { + if ($this->macros[$node->name][2] && $node->htmlNode) { + $node->isEmpty = TRUE; + $this->compiler->setContext(Latte\Compiler::CONTEXT_DOUBLE_QUOTED); + $res = $this->compile($node, $this->macros[$node->name][2]); + $this->compiler->setContext(NULL); + if (!$node->attrCode) { + $node->attrCode = ""; + } + } else { + $node->isEmpty = !isset($this->macros[$node->name][1]); + $res = $this->compile($node, $this->macros[$node->name][0]); + if (!$node->openingCode) { + $node->openingCode = ""; + } + } + return $res !== FALSE; + } + + + + /** + * Node is closed. + * @return void + */ + public function nodeClosed(MacroNode $node) + { + $res = $this->compile($node, $this->macros[$node->name][1]); + if (!$node->closingCode) { + $node->closingCode = ""; + } + } + + + + /** + * Generates code. + * @return string + */ + private function compile(MacroNode $node, $def) + { + $node->tokenizer->reset(); + $writer = Latte\PhpWriter::using($node, $this->compiler); + if (is_string($def)/*5.2* && substr($def, 0, 1) !== "\0"*/) { + return $writer->write($def); + } else { + return Nette\Callback::create($def)->invoke($node, $writer); + } + } + + + + /** + * @return Latte\Compiler + */ + public function getCompiler() + { + return $this->compiler; + } + +} diff --git a/libs/Nette/Latte/Macros/UIMacros.php b/libs/Nette/Latte/Macros/UIMacros.php new file mode 100644 index 0000000..b20ae4c --- /dev/null +++ b/libs/Nette/Latte/Macros/UIMacros.php @@ -0,0 +1,520 @@ +addMacro('include', array($me, 'macroInclude')); + $me->addMacro('includeblock', array($me, 'macroIncludeBlock')); + $me->addMacro('extends', array($me, 'macroExtends')); + $me->addMacro('layout', array($me, 'macroExtends')); + $me->addMacro('block', array($me, 'macroBlock'), array($me, 'macroBlockEnd')); + $me->addMacro('define', array($me, 'macroBlock'), array($me, 'macroBlockEnd')); + $me->addMacro('snippet', array($me, 'macroBlock'), array($me, 'macroBlockEnd')); + $me->addMacro('ifset', array($me, 'macroIfset'), 'endif'); + + $me->addMacro('widget', array($me, 'macroControl')); // deprecated - use control + $me->addMacro('control', array($me, 'macroControl')); + + $me->addMacro('href', NULL, NULL, function(MacroNode $node, PhpWriter $writer) use ($me) { + return ' ?> href="macroLink($node, $writer) . ' ?>"addMacro('plink', array($me, 'macroLink')); + $me->addMacro('link', array($me, 'macroLink')); + $me->addMacro('ifCurrent', array($me, 'macroIfCurrent'), 'endif'); // deprecated; use n:class="$presenter->linkCurrent ? ..." + + $me->addMacro('contentType', array($me, 'macroContentType')); + $me->addMacro('status', array($me, 'macroStatus')); + } + + + + /** + * Initializes before template parsing. + * @return void + */ + public function initialize() + { + $this->namedBlocks = array(); + $this->extends = NULL; + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + // try close last block + try { + $this->getCompiler()->writeMacro('/block'); + } catch (CompileException $e) { + } + + $epilog = $prolog = array(); + + if ($this->namedBlocks) { + foreach ($this->namedBlocks as $name => $code) { + $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name); + $snippet = $name[0] === '_'; + $prolog[] = "//\n// block $name\n//\n" + . "if (!function_exists(\$_l->blocks[" . var_export($name, TRUE) . "][] = '$func')) { " + . "function $func(\$_l, \$_args) { " + . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v') // PHP bug #46873 + . ($snippet ? '; $_control->validateControl(' . var_export(substr($name, 1), TRUE) . ')' : '') + . "\n?>$codenamedBlocks || $this->extends) { + $prolog[] = "// template extending and snippets support"; + + $prolog[] = '$_l->extends = ' + . ($this->extends ? $this->extends : 'empty($template->_extended) && isset($_control) && $_control instanceof Nette\Application\UI\Presenter ? $_control->findLayoutTemplateFile() : NULL') + . '; $template->_extended = $_extended = TRUE;'; + + $prolog[] = ' +if ($_l->extends) { + ' . ($this->namedBlocks ? 'ob_start();' : 'return Nette\Latte\Macros\CoreMacros::includeTemplate($_l->extends, get_defined_vars(), $template)->render();') . ' + +} elseif (!empty($_control->snippetMode)) { + return Nette\Latte\Macros\UIMacros::renderSnippets($_control, $_l, get_defined_vars()); +}'; + } else { + $prolog[] = ' +// snippets support +if (!empty($_control->snippetMode)) { + return Nette\Latte\Macros\UIMacros::renderSnippets($_control, $_l, get_defined_vars()); +}'; + } + + return array(implode("\n\n", $prolog), implode("\n", $epilog)); + } + + + + /********************* macros ****************d*g**/ + + + + /** + * {include #block} + */ + public function macroInclude(MacroNode $node, PhpWriter $writer) + { + $destination = $node->tokenizer->fetchWord(); // destination [,] [params] + if (substr($destination, 0, 1) !== '#') { + return FALSE; + } + + $destination = ltrim($destination, '#'); + $parent = $destination === 'parent'; + if ($destination === 'parent' || $destination === 'this') { + for ($item = $node->parentNode; $item && $item->name !== 'block' && !isset($item->data->name); $item = $item->parentNode); + if (!$item) { + throw new CompileException("Cannot include $destination block outside of any block."); + } + $destination = $item->data->name; + } + + $name = Strings::contains($destination, '$') ? $destination : var_export($destination, TRUE); + if (isset($this->namedBlocks[$destination]) && !$parent) { + $cmd = "call_user_func(reset(\$_l->blocks[$name]), \$_l, %node.array? + get_defined_vars())"; + } else { + $cmd = 'Nette\Latte\Macros\UIMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_l, $name, %node.array? + " . ($parent ? 'get_defined_vars' : '$template->getParameters') . '())'; + } + + if ($node->modifiers) { + return $writer->write("ob_start(); $cmd; echo %modify(ob_get_clean())"); + } else { + return $writer->write($cmd); + } + } + + + + /** + * {includeblock "file"} + */ + public function macroIncludeBlock(MacroNode $node, PhpWriter $writer) + { + return $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + get_defined_vars(), $_l->templates[%var])->render()', + $this->getCompiler()->getTemplateId()); + } + + + + /** + * {extends auto | none | $var | "file"} + */ + public function macroExtends(MacroNode $node, PhpWriter $writer) + { + if (!$node->args) { + throw new CompileException("Missing destination in {extends}"); + } + if (!empty($node->parentNode)) { + throw new CompileException("{extends} must be placed outside any macro."); + } + if ($this->extends !== NULL) { + throw new CompileException("Multiple {extends} declarations are not allowed."); + } + if ($node->args === 'none') { + $this->extends = 'FALSE'; + } elseif ($node->args === 'auto') { + $this->extends = '$_presenter->findLayoutTemplateFile()'; + } else { + $this->extends = $writer->write('%node.word%node.args'); + } + return; + } + + + + /** + * {block [[#]name]} + * {snippet [name [,]] [tag]} + * {define [#]name} + */ + public function macroBlock(MacroNode $node, PhpWriter $writer) + { + $name = $node->tokenizer->fetchWord(); + + if ($node->name === 'block' && $name === FALSE) { // anonymous block + return $node->modifiers === '' ? '' : 'ob_start()'; + } + + $node->data->name = $name = ltrim($name, '#'); + if ($name == NULL) { + if ($node->name !== 'snippet') { + throw new CompileException("Missing block name."); + } + + } elseif (Strings::contains($name, '$')) { // dynamic block/snippet + if ($node->name === 'snippet') { + for ($parent = $node->parentNode; $parent && $parent->name !== 'snippet'; $parent = $parent->parentNode); + if (!$parent) { + throw new CompileException("Dynamic snippets are allowed only inside static snippet."); + } + $parent->data->dynamic = TRUE; + $node->data->leave = TRUE; + $node->closingCode = ""; + + if ($node->htmlNode) { + $node->attrCode = $writer->write("getSnippetId({$writer->formatWord($name)})) . '\"' ?>"); + return $writer->write('ob_start()'); + } + $tag = trim($node->tokenizer->fetchWord(), '<>'); + $tag = $tag ? $tag : 'div'; + $node->closingCode .= "\n"; + return $writer->write("?>\n<$tag id=\"getSnippetId({$writer->formatWord($name)}) ?>\">data->leave = TRUE; + $fname = $writer->formatWord($name); + $node->closingCode = "name === 'define' ? '' : "call_user_func(reset(\$_l->blocks[$fname]), \$_l, get_defined_vars())") . " ?>"; + $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name); + return "\n\n//\n// block $name\n//\n" + . "if (!function_exists(\$_l->blocks[$fname]['{$this->getCompiler()->getTemplateId()}'] = '$func')) { " + . "function $func(\$_l, \$_args) { " + . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v'); // PHP bug #46873 + } + } + + // static block/snippet + if ($node->name === 'snippet') { + $node->data->name = $name = '_' . $name; + } + + if (isset($this->namedBlocks[$name])) { + throw new CompileException("Cannot redeclare static block '$name'"); + } + + $prolog = $this->namedBlocks ? '' : "if (\$_l->extends) { ob_end_clean(); return Nette\\Latte\\Macros\\CoreMacros::includeTemplate(\$_l->extends, get_defined_vars(), \$template)->render(); }\n"; + $top = empty($node->parentNode); + $this->namedBlocks[$name] = TRUE; + + $include = 'call_user_func(reset($_l->blocks[%var]), $_l, ' . ($node->name === 'snippet' ? '$template->getParameters()' : 'get_defined_vars()') . ')'; + if ($node->modifiers) { + $include = "ob_start(); $include; echo %modify(ob_get_clean())"; + } + + if ($node->name === 'snippet') { + if ($node->htmlNode) { + $node->attrCode = $writer->write('getSnippetId(%var) . \'"\' ?>', (string) substr($name, 1)); + return $writer->write($prolog . $include, $name); + } + $tag = trim($node->tokenizer->fetchWord(), '<>'); + $tag = $tag ? $tag : 'div'; + return $writer->write("$prolog ?>\n<$tag id=\"getSnippetId(%var) ?>\">\nname === 'define') { + return $prolog; + + } else { + return $writer->write($prolog . $include, $name); + } + } + + + + /** + * {/block} + * {/snippet} + * {/define} + */ + public function macroBlockEnd(MacroNode $node, PhpWriter $writer) + { + if (isset($node->data->name)) { // block, snippet, define + if ($node->name === 'snippet' && $node->htmlNode && !$node->prefix // n:snippet -> n:inner-snippet + && preg_match("#^.*? n:\w+>\n?#s", $node->content, $m1) && preg_match("#[ \t]*<[^<]+$#sD", $node->content, $m2)) + { + $node->openingCode = $m1[0] . $node->openingCode; + $node->content = substr($node->content, strlen($m1[0]), -strlen($m2[0])); + $node->closingCode .= $m2[0]; + } + + if (empty($node->data->leave)) { + if (!empty($node->data->dynamic)) { + $node->content .= ''; + } + $this->namedBlocks[$node->data->name] = $tmp = rtrim(ltrim($node->content, "\n"), " \t"); + $node->content = substr_replace($node->content, $node->openingCode . "\n", strspn($node->content, "\n"), strlen($tmp)); + $node->openingCode = ""; + } + + } elseif ($node->modifiers) { // anonymous block with modifier + return $writer->write('echo %modify(ob_get_clean())'); + } + } + + + + /** + * {ifset #block} + */ + public function macroIfset(MacroNode $node, PhpWriter $writer) + { + if (!Strings::contains($node->args, '#')) { + return FALSE; + } + $list = array(); + while (($name = $node->tokenizer->fetchWord()) !== FALSE) { + $list[] = $name[0] === '#' ? '$_l->blocks["' . substr($name, 1) . '"]' : $name; + } + return 'if (isset(' . implode(', ', $list) . ')):'; + } + + + + /** + * {control name[:method] [params]} + */ + public function macroControl(MacroNode $node, PhpWriter $writer) + { + $pair = $node->tokenizer->fetchWord(); + if ($pair === FALSE) { + throw new CompileException("Missing control name in {control}"); + } + $pair = explode(':', $pair, 2); + $name = $writer->formatWord($pair[0]); + $method = isset($pair[1]) ? ucfirst($pair[1]) : ''; + $method = Strings::match($method, '#^\w*$#') ? "render$method" : "{\"render$method\"}"; + $param = $writer->formatArray(); + if (!Strings::contains($node->args, '=>')) { + $param = substr($param, 6, -1); // removes array() + } + return ($name[0] === '$' ? "if (is_object($name)) \$_ctrl = $name; else " : '') + . '$_ctrl = $_control->getComponent(' . $name . '); ' + . 'if ($_ctrl instanceof Nette\Application\UI\IRenderable) $_ctrl->validateControl(); ' + . "\$_ctrl->$method($param)"; + } + + + + /** + * {link destination [,] [params]} + * {plink destination [,] [params]} + * n:href="destination [,] [params]" + */ + public function macroLink(MacroNode $node, PhpWriter $writer) + { + return $writer->write('echo %escape(%modify(' . ($node->name === 'plink' ? '$_presenter' : '$_control') . '->link(%node.word, %node.array?)))'); + } + + + + /** + * {ifCurrent destination [,] [params]} + */ + public function macroIfCurrent(MacroNode $node, PhpWriter $writer) + { + return $writer->write(($node->args ? 'try { $_presenter->link(%node.word, %node.array?); } catch (Nette\Application\UI\InvalidLinkException $e) {}' : '') + . '; if ($_presenter->getLastCreatedRequestFlag("current")):'); + } + + + + /** + * {contentType ...} + */ + public function macroContentType(MacroNode $node, PhpWriter $writer) + { + if (Strings::contains($node->args, 'xhtml')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XHTML); + + } elseif (Strings::contains($node->args, 'html')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_HTML); + + } elseif (Strings::contains($node->args, 'xml')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XML); + + } elseif (Strings::contains($node->args, 'javascript')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_JS); + + } elseif (Strings::contains($node->args, 'css')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_CSS); + + } elseif (Strings::contains($node->args, 'calendar')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_ICAL); + + } else { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_TEXT); + } + + // temporary solution + if (Strings::contains($node->args, '/')) { + return $writer->write('$netteHttpResponse->setHeader("Content-Type", %var)', $node->args); + } + } + + + + /** + * {status ...} + */ + public function macroStatus(MacroNode $node, PhpWriter $writer) + { + return $writer->write((substr($node->args, -1) === '?' ? 'if (!$netteHttpResponse->isSent()) ' : '') . + '$netteHttpResponse->setCode(%var)', (int) $node->args + ); + } + + + + /********************* run-time writers ****************d*g**/ + + + + /** + * Calls block. + * @return void + */ + public static function callBlock(\stdClass $context, $name, array $params) + { + if (empty($context->blocks[$name])) { + throw new Nette\InvalidStateException("Cannot include undefined block '$name'."); + } + $block = reset($context->blocks[$name]); + $block($context, $params); + } + + + + /** + * Calls parent block. + * @return void + */ + public static function callBlockParent(\stdClass $context, $name, array $params) + { + if (empty($context->blocks[$name]) || ($block = next($context->blocks[$name])) === FALSE) { + throw new Nette\InvalidStateException("Cannot include undefined parent block '$name'."); + } + $block($context, $params); + } + + + + public static function renderSnippets(Nette\Application\UI\Control $control, \stdClass $local, array $params) + { + $control->snippetMode = FALSE; + $payload = $control->getPresenter()->getPayload(); + if (isset($local->blocks)) { + foreach ($local->blocks as $name => $function) { + if ($name[0] !== '_' || !$control->isControlInvalid(substr($name, 1))) { + continue; + } + ob_start(); + $function = reset($function); + $snippets = $function($local, $params); + $payload->snippets[$id = $control->getSnippetId(substr($name, 1))] = ob_get_clean(); + if ($snippets) { + $payload->snippets += $snippets; + unset($payload->snippets[$id]); + } + } + } + $control->snippetMode = TRUE; + if ($control instanceof Nette\Application\UI\IRenderable) { + $queue = array($control); + do { + foreach (array_shift($queue)->getComponents() as $child) { + if ($child instanceof Nette\Application\UI\IRenderable) { + if ($child->isControlInvalid()) { + $child->snippetMode = TRUE; + $child->render(); + $child->snippetMode = FALSE; + } + } elseif ($child instanceof Nette\ComponentModel\IContainer) { + $queue[] = $child; + } + } + } while ($queue); + } + } + +} diff --git a/libs/Nette/Latte/Parser.php b/libs/Nette/Latte/Parser.php index 1402616..df2f8b5 100644 --- a/libs/Nette/Latte/Parser.php +++ b/libs/Nette/Latte/Parser.php @@ -1,595 +1,405 @@ -. - * @param string - * @return string - */ - public function parse($s) - { - if (!Strings::checkEncoding($s)) { - throw new ParseException('Template is not valid UTF-8 stream.'); - } - if (!$this->macroRe) { - $this->setDelimiters('\\{(?![\\s\'"{}*])', '\\}'); - } - $this->handler->initialize($this); - - $s = str_replace("\r\n", "\n", $s); - $s = "\n" . $s; - - $this->input = & $s; - $this->offset = 0; - $this->output = ''; - $this->htmlNodes = $this->macroNodes = array(); - $len = strlen($s); - - while ($this->offset < $len) { - $matches = $this->{"context$this->context"}(); - - if (!$matches) { // EOF - break; - - } elseif (!empty($matches['comment'])) { // {* *} - - } elseif (!empty($matches['macro'])) { // {macro} - list($macroName, $macroArgs, $macroModifiers) = $this->parseMacro($matches['macro']); - $code = $this->macro($macroName, $macroArgs, $macroModifiers); - $nl = isset($matches['newline']) ? "\n" : ''; - if ($nl && $matches['indent'] && strncmp($code, 'output .= "\n" . $code; // preserve new line from 'indent', remove indentation - } else { - // double newline to avoid newline eating by PHP - $this->output .= $matches['indent'] . $code . (substr($code, -2) === '?>' && $this->output !== '' ? $nl : ''); - } - - } else { // common behaviour - $this->output .= $matches[0]; - } - } - - $this->output .= substr($this->input, $this->offset); - - foreach ($this->htmlNodes as $node) { - if (!$node instanceof MacroNode && !empty($node->attrs)) { - throw new ParseException("Missing end tag name> for macro-attribute " . self::HTML_PREFIX - . implode(' and ' . self::HTML_PREFIX, array_keys($node->attrs)) . ".", 0, $this->line); - } - } - - $this->handler->finalize($this->output); - - if ($this->macroNodes) { - throw new ParseException("There are unclosed macros.", 0, $this->line); - } - - return $this->output; - } - - - - /** - * Handles CONTEXT_TEXT. - */ - private function contextText() - { - $matches = $this->match('~ - (?:(?<=\n)[ \t]*)?<(?P/?)(?P[a-z0-9:]+)| ## begin of HTML tag !--)| ## begin of HTML comment