#!/usr/bin/env php ')) { fwrite( STDERR, sprintf( 'PHPUnit 8.1.5 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . 'This version of PHPUnit is supported on PHP 7.2 and PHP 7.3.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, PHP_VERSION, PHP_BINARY ) ); die(1); } if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { $execute = true; } else { $execute = false; } $options = getopt('', array('prepend:', 'manifest')); if (isset($options['prepend'])) { require $options['prepend']; } if (isset($options['manifest'])) { $printManifest = true; } unset($options); define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__)); define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-8.1.5.phar'); Phar::mapPhar('phpunit-8.1.5.phar'); require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/DeepCopy.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Filter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php'; require 'phar://phpunit-8.1.5.phar' . '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php'; require 'phar://phpunit-8.1.5.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php'; require 'phar://phpunit-8.1.5.phar' . '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Assert.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SelfDescribing.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/AssertionFailedError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/CodeCoverageException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/Constraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ArrayHasKey.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ArraySubset.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/Composite.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/Attribute.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/Callback.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ClassHasAttribute.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ClassHasStaticAttribute.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/Count.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/DirectoryExists.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ExceptionCode.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ExceptionMessage.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ExceptionMessageRegularExpression.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/FileExists.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/GreaterThan.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsAnything.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsEmpty.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsEqual.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsFalse.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsFinite.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsIdentical.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsInfinite.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsInstanceOf.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsJson.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsNan.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsNull.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsReadable.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsTrue.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsType.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/IsWritable.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/JsonMatches.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/LessThan.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/LogicalAnd.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/LogicalNot.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/LogicalOr.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/LogicalXor.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/ObjectHasAttribute.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/RegularExpression.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/SameSize.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/StringContains.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/StringEndsWith.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/StringMatchesFormatDescription.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/StringStartsWith.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/TraversableContains.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Constraint/TraversableContainsOnly.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/RiskyTest.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/RiskyTestError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/CoveredCodeNotExecutedException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Test.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestSuite.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/DataProviderTestSuite.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Error/Error.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Error/Deprecated.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Error/Notice.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Error/Warning.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/ExceptionWrapper.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/ExpectationFailedException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/IncompleteTest.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestCase.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/IncompleteTestCase.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/IncompleteTestError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/InvalidCoversTargetException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/InvalidDataProviderException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MissingCoversAnnotationException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Exception/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/Identity.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/Stub.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/Match.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/ParametersMatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/InvocationMocker.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Builder/NamespaceMatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Generator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Invocation/Invocation.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Verifiable.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Invokable.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/MatcherCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/InvocationMocker.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Invocation/StaticInvocation.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Invocation/ObjectInvocation.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/Invocation.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedRecorder.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/AnyInvokedCount.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/StatelessInvocation.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/AnyParameters.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/ConsecutiveParameters.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/DeferredError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtIndex.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtLeastCount.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtMostCount.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedCount.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/MethodName.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Matcher/Parameters.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/MockBuilder.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/MockMethod.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/MockMethodSet.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/MockObject.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Exception/RuntimeException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ReturnArgument.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ReturnCallback.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ReturnReference.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ReturnSelf.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ReturnStub.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/OutputError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SyntheticError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/PHPTAssertionFailedError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SkippedTest.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SkippedTestCase.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SkippedTestError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SkippedTestSuiteError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/SyntheticSkippedError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestFailure.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestListener.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestListenerDefaultImplementation.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestResult.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/TestSuiteIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/UnintentionallyCoveredCodeError.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/Warning.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Framework/WarningTestCase.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/Hook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/TestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterIncompleteTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterLastTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterRiskyTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterSkippedTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterTestErrorHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterTestFailureHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/AfterTestWarningHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/BaseTestRunner.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/BeforeFirstTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/BeforeTestHook.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/TestResultCache.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/DefaultTestResultCache.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Filter/GroupFilterIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Filter/Factory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Filter/NameFilterIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/NullTestResultCache.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/PhptTestCase.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/ResultCacheExtension.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/TestSuiteLoader.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/StandardTestSuiteLoader.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Hook/TestListenerAdapter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/TestSuiteSorter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Runner/Version.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/TextUI/Command.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/TextUI/Help.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Printer.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/TextUI/ResultPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/TextUI/TestRunner.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Blacklist.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Color.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Configuration.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/ConfigurationGenerator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/ErrorHandler.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/FileLoader.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Filesystem.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Filter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Getopt.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/GlobalState.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/InvalidArgumentHelper.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Json.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Log/JUnit.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Log/TeamCity.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/PHP/AbstractPhpProcess.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/PHP/DefaultPhpProcess.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/PHP/WindowsPhpProcess.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/RegularExpression.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Test.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/TestDoxPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/CliTestDoxPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/ResultPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/HtmlResultPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/NamePrettifier.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/TextResultPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TestDox/XmlResultPrinter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/TextTestListRenderer.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Type.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/XdebugFilterScriptGenerator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/Xml.php'; require 'phar://phpunit-8.1.5.phar' . '/phpunit/Util/XmlTestListRenderer.php'; require 'phar://phpunit-8.1.5.phar' . '/php-token-stream/Token.php'; require 'phar://phpunit-8.1.5.phar' . '/php-token-stream/Token/Stream.php'; require 'phar://phpunit-8.1.5.phar' . '/php-token-stream/Token/Stream/CachingFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Type.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Application.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/ApplicationName.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Author.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/AuthorCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/AuthorCollectionIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ManifestElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/AuthorElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ElementCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/AuthorElementCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/BundledComponent.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/BundledComponentCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/BundledComponentCollectionIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/BundlesElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ComponentElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ComponentElementCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ContainsElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/CopyrightElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/CopyrightInformation.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Email.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ExtElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ExtElementCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Extension.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ExtensionElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/InvalidApplicationNameException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/InvalidEmailException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/InvalidUrlException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Library.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/License.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/LicenseElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Manifest.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ManifestDocument.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/ManifestDocumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/ManifestDocumentLoadingException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/ManifestDocumentMapper.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/ManifestElementException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/ManifestLoader.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/exceptions/ManifestLoaderException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/ManifestSerializer.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/PhpElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Requirement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/PhpExtensionRequirement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/PhpVersionRequirement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/RequirementCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/RequirementCollectionIterator.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/xml/RequiresElement.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-manifest/values/Url.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/VersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/AbstractVersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/AndVersionConstraintGroup.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/AnyVersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/ExactVersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/exceptions/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/exceptions/InvalidVersionException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/OrVersionConstraintGroup.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/PreReleaseSuffix.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/constraints/SpecificMajorVersionConstraint.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/Version.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/VersionConstraintParser.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/VersionConstraintValue.php'; require 'phar://phpunit-8.1.5.phar' . '/phar-io-version/VersionNumber.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Call/Call.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Call/CallCenter.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/Comparator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/Factory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Comparator/Factory.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/ArrayComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/ObjectComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Doubler.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Prophet.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Util/ExportUtil.php'; require 'phar://phpunit-8.1.5.phar' . '/phpspec-prophecy/Prophecy/Util/StringUtil.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/CodeCoverage.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Exception/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Exception/RuntimeException.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Exception/CoveredCodeNotExecutedException.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Driver/Driver.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Driver/PCOV.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Driver/PHPDBG.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Driver/Xdebug.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Filter.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Exception/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Exception/MissingCoversAnnotationException.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Node/AbstractNode.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Node/Builder.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Node/Directory.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Node/File.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Node/Iterator.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Clover.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Crap4j.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Html/Renderer.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Html/Renderer/Dashboard.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Html/Renderer/Directory.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Html/Facade.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Html/Renderer/File.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/PHP.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Text.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/BuildInformation.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Coverage.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Node.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Directory.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Facade.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/File.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Method.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Project.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Report.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Source.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Tests.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Totals.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Report/Xml/Unit.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Util.php'; require 'phar://phpunit-8.1.5.phar' . '/php-code-coverage/Version.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-code-unit-reverse-lookup/Wizard.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/ComparisonFailure.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/DOMNodeComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/DateTimeComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/ScalarComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/NumericComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/DoubleComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/ExceptionComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/MockObjectComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/ResourceComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/SplObjectStorageComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-comparator/TypeComparator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Chunk.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Exception/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Exception/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Exception/ConfigurationException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Diff.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Differ.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Line.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/LongestCommonSubsequenceCalculator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Output/DiffOutputBuilderInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Output/AbstractChunkOutputBuilder.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Output/DiffOnlyOutputBuilder.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/Parser.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-environment/Console.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-environment/OperatingSystem.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-environment/Runtime.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-exporter/Exporter.php'; require 'phar://phpunit-8.1.5.phar' . '/php-file-iterator/Facade.php'; require 'phar://phpunit-8.1.5.phar' . '/php-file-iterator/Factory.php'; require 'phar://phpunit-8.1.5.phar' . '/php-file-iterator/Iterator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-global-state/Blacklist.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-global-state/CodeExporter.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-global-state/exceptions/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-global-state/Restorer.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-global-state/exceptions/RuntimeException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-global-state/Snapshot.php'; require 'phar://phpunit-8.1.5.phar' . '/php-invoker/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/php-invoker/Invoker.php'; require 'phar://phpunit-8.1.5.phar' . '/php-invoker/TimeoutException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-object-enumerator/Enumerator.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-object-enumerator/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-object-enumerator/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-object-reflector/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-object-reflector/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-object-reflector/ObjectReflector.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-recursion-context/Context.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-recursion-context/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-recursion-context/InvalidArgumentException.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-resource-operations/ResourceOperations.php'; require 'phar://phpunit-8.1.5.phar' . '/php-timer/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/php-timer/RuntimeException.php'; require 'phar://phpunit-8.1.5.phar' . '/php-timer/Timer.php'; require 'phar://phpunit-8.1.5.phar' . '/sebastian-version/Version.php'; require 'phar://phpunit-8.1.5.phar' . '/php-text-template/Template.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/Exception.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/NamespaceUri.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/NamespaceUriException.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/Token.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/TokenCollection.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/TokenCollectionException.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/Tokenizer.php'; require 'phar://phpunit-8.1.5.phar' . '/theseer-tokenizer/XMLSerializer.php'; require 'phar://phpunit-8.1.5.phar' . '/webmozart-assert/Assert.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Description.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tag.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-common/Element.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-common/File.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-common/Fqsen.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/FqsenResolver.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-common/Location.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-common/Project.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-reflection-common/ProjectFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Type.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/TypeResolver.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Array_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Boolean.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Callable_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Compound.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Context.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/ContextFactory.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Float_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Integer.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Iterable_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Mixed_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Null_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Nullable.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Object_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Parent_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Resource_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Scalar.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Self_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Static_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/String_.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/This.php'; require 'phar://phpunit-8.1.5.phar' . '/phpdocumentor-type-resolver/Types/Void_.php'; if ($execute) { if (isset($printManifest)) { print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); exit; } unset($execute); PHPUnit\TextUI\Command::main(); } __HALT_COMPILER(); ?> {phpunit-8.1.5.pharsebastian-exporter/Exporter.php#J\#!Ysebastian-exporter/LICENSEJ\`0phpspec-prophecy/Prophecy/Comparator/Factory.phpJ\ֈi;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.phpsJ\shǶ:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpKJ\K)RQ-phpspec-prophecy/Prophecy/Call/CallCenter.php*J\*.w'phpspec-prophecy/Prophecy/Call/Call.php J\ 8Vuٶ:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.phpJ\Fh<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpJ\rAphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.phpJ\pb<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpJ\ NvBphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.phpJ\#I;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.phpJ\bN/;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.phpJ\ٰ@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.phpJ\|@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.phpJ\6phpspec-prophecy/Prophecy/Argument/Token/TypeToken.phpJ\n\:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php,J\,cR̶=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php9 J\9 E.<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php J\ 3<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.phpJ\4̶<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.phpJ\J:8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php4 J\4 A;K25phpspec-prophecy/Prophecy/Promise/CallbackPromise.phpJ\[;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'J\'(2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php] J\] ~6phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpKJ\K3phpspec-prophecy/Prophecy/Promise/ReturnPromise.phpJ\؏5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.phpq1J\q1?5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.phpJ\5D8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpHJ\HgZ8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php,J\,W?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.phpJ\i/phpspec-prophecy/Prophecy/Prophecy/Revealer.phpJ\jɸ-phpspec-prophecy/Prophecy/Doubler/Doubler.phpJ\8]^5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.phpJ\8dj3phpspec-prophecy/Prophecy/Doubler/NameGenerator.phpJ\70phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpF J\F l3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.phpJ\̇gBphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.phpJ\TH;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.phpfJ\fei?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.phpJ\5HAphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpJ\|>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.phpJ\<[<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.phpJ\?BrCphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.phpJ\Aphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.phpJ\t>˶Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php~ J\~ TDphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phplJ\l)5:Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php J\ jNHphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.phpJ\:0`Pphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phppJ\pxCphpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.phpJ\x^=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php$ J\$ AA?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.phpa J\a ?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php J\ _aGkEphpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php J\ ZGphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.phpJ\ ;Iphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.phpxJ\xrЬCphpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.phpoJ\ou9=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.phpDJ\Dd9϶Dphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.phpJ\Hphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php)J\)F4Bphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.phpJ\$϶Hphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.phpJ\:F@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.phpJ\Z^?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.phpJ\zFEphpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.phpJ\77/%Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.phpJ\h+Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.phpDJ\DpGphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.phpJ\XDphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.phpJ\ۉ?Jphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.phpJ\Jphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.phpJ\1phpspec-prophecy/Prophecy/Exception/Exception.php+J\+@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.phpJ\gFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.phpJ\2TѶCphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.phpJ\l<Pphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.phpJ\ ƶLphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpJJ\J~DEphpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.phpJ\;Kphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php,J\,a%phpspec-prophecy/Prophecy/Prophet.phpbJ\bZ`-phpspec-prophecy/Prophecy/Util/ExportUtil.phpPJ\P2qƶ-phpspec-prophecy/Prophecy/Util/StringUtil.php J\ Z~&phpspec-prophecy/Prophecy/Argument.phpJ\AT:phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.phpJ\L9%<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php J\ X<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.phpJ\`IE7phpspec-prophecy/Prophecy/Prediction/CallPrediction.phpQ J\Q I;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.phpJ\Vb{ζphpspec-prophecy/LICENSE}J\}6*sebastian-comparator/ComparisonFailure.php J\ I(sebastian-comparator/ArrayComparator.phpJ\Lj:Q sebastian-comparator/Factory.php J\ o'sebastian-comparator/TypeComparator.phpJ\SҶ+sebastian-comparator/ResourceComparator.php.J\.K*sebastian-comparator/NumericComparator.phpJ\=Զ3sebastian-comparator/SplObjectStorageComparator.phpJ\ #sebastian-comparator/Comparator.php\J\\89+sebastian-comparator/DateTimeComparator.php J\ ~ȶ)sebastian-comparator/DoubleComparator.phptJ\t^k,sebastian-comparator/ExceptionComparator.phpJ\ѕ)sebastian-comparator/ScalarComparator.php J\ QM)sebastian-comparator/ObjectComparator.phpJ\92-sebastian-comparator/MockObjectComparator.phpPJ\PPN*sebastian-comparator/DOMNodeComparator.php J\ 8sebastian-comparator/LICENSE J\ q7myclabs-deep-copy/DeepCopy/Exception/CloneException.phpJ\Lt:myclabs-deep-copy/DeepCopy/Exception/PropertyException.phpxJ\x4:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php~J\~:̶Bmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phpJ\Lmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phpJ\7߶Gmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpJ\:D3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.phpJ\a˲0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.phpJ\3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.phpJ\]],myclabs-deep-copy/DeepCopy/Filter/Filter.php\J\\6S6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.phpJ\swֶ'myclabs-deep-copy/DeepCopy/DeepCopy.phpJ\r֎;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.phpJ\ZٶAmyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.phpJ\GFжAmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpJ\̶Gmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.phpJ\J霶4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.phpJ\-h7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php J\ 7Dmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpoJ\o?3:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.phpJ\Yx.myclabs-deep-copy/DeepCopy/Matcher/Matcher.phpJ\qe:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php&J\&6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.phpJ\?Sb(myclabs-deep-copy/DeepCopy/deep_copy.phpJ\"emyclabs-deep-copy/LICENSE5J\5ʭ˄php-text-template/Template.php J\ w4php-text-template/LICENSE J\ S:#sebastian-global-state/Restorer.phpJ\6k/sebastian-global-state/exceptions/Exception.phpNJ\Nch56sebastian-global-state/exceptions/RuntimeException.phpJ\pƶ'sebastian-global-state/CodeExporter.phpj J\j 7gѶ#sebastian-global-state/Snapshot.php>)J\>))ɶ$sebastian-global-state/Blacklist.php J\ =ŵ@sebastian-global-state/LICENSEJ\?K<doctrine-instantiator/Doctrine/Instantiator/Instantiator.phpJ\Rdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.phpJ\EwٶRdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php|J\|3Ldoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.phpJ\fEdoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.phpJ\]Ddoctrine-instantiator/LICENSE$J\$ ͂.phpdocumentor-reflection-docblock/DocBlock.phpJ\Z:phpdocumentor-reflection-docblock/DocBlock/Description.php/J\/> Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php,J\,8(Aphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.phpMJ\M|Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.phpJ\c@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php J\ 2;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.phpJ\@;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php J\ \Rphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php+J\+ܶLphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.phpJ\<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php J\ ]]H:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.phpn J\n С:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php J\ 8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php J\ ϶;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.phppJ\pHAphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php J\ sȶ>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php J\ 9phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php|J\|:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.phpJ\~|:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.phpJ\Ȉض9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php J\ yaö;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php_ J\_ (oٶ8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.phpNJ\NV8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.phpPJ\PT,Hphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.phpJ\2iDphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.phpJ\R=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.phpJ\Dy7;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.phpJ\X c7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php J\ y%:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.phpFJ\F.h)Aphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.phppJ\p3{<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.phpJ\x2phpdocumentor-reflection-docblock/DocBlock/Tag.phpuJ\u⹰Aphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.phpR.J\R.Ø9phpdocumentor-reflection-docblock/DocBlock/TagFactory.phpJ\P;Ͷ9phpdocumentor-reflection-docblock/DocBlock/Serializer.phpJ\"~.0>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php!J\!})phpdocumentor-reflection-docblock/LICENSE8J\8ʶ5phpdocumentor-reflection-docblock/DocBlockFactory.php$J\$Ńphp-timer/Timer.php J\ php-timer/Exception.phpCJ\C/uphp-timer/RuntimeException.php{J\{%php-timer/LICENSEJ\]Ut4sebastian-resource-operations/ResourceOperations.phpiJ\i6G%sebastian-resource-operations/LICENSEJ\r+phar-io-version/VersionConstraintParser.php J\ ^.phar-io-version/Version.php^J\^[A+(phar-io-version/exceptions/Exception.phplJ\l؈Dphar-io-version/exceptions/UnsupportedVersionConstraintException.phpJ\`r?phar-io-version/exceptions/InvalidPreReleaseSuffixException.phpvJ\v6phar-io-version/exceptions/InvalidVersionException.php{J\{O$phar-io-version/PreReleaseSuffix.phpgJ\gd*phar-io-version/VersionConstraintValue.php J\ 1phar-io-version/constraints/VersionConstraint.php6J\6wU9phar-io-version/constraints/AbstractVersionConstraint.phpJ\Whg>phar-io-version/constraints/SpecificMajorVersionConstraint.phpuJ\ur6phar-io-version/constraints/ExactVersionConstraint.phpZJ\Z Ephar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php J\  4phar-io-version/constraints/AnyVersionConstraint.phpJ\8phar-io-version/constraints/OrVersionConstraintGroup.php(J\(9phar-io-version/constraints/AndVersionConstraintGroup.php*J\* ChFphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php[J\[@n!phar-io-version/VersionNumber.php"J\"v޶phar-io-version/LICENSE1J\1>: php-invoker/TimeoutException.phpxJ\xI-php-invoker/Exception.php@J\@'php-invoker/Invoker.phpJ\ ̶)sebastian-object-enumerator/Exception.php6J\6n$*a8sebastian-object-enumerator/InvalidArgumentException.phpxJ\x'*sebastian-object-enumerator/Enumerator.phprJ\rz\!sebastian-environment/Console.phpJ\Eܶ)sebastian-environment/OperatingSystem.phpJ\`sebastian-environment/LICENSEJ\I!sebastian-environment/Runtime.phpdJ\dHʱ manifest.txtfJ\fK object-reflector/LICENSE J\ F,phpdocumentor-type-resolver/TypeResolver.phpw$J\w$Uy$phpdocumentor-type-resolver/Type.phpJ\[L-phpdocumentor-type-resolver/FqsenResolver.php J\ Rٶ#phpdocumentor-type-resolver/LICENSE8J\8ʶ*phpdocumentor-type-resolver/Types/This.phpJ\h²+phpdocumentor-type-resolver/Types/Void_.phpWJ\Wֶ-phpdocumentor-type-resolver/Types/String_.phpJ\,phpdocumentor-type-resolver/Types/Scalar.phpJ\U/phpdocumentor-type-resolver/Types/Callable_.phpJ\4ɿ-phpdocumentor-type-resolver/Types/Integer.phpJ\"s-phpdocumentor-type-resolver/Types/Boolean.phpJ\f,phpdocumentor-type-resolver/Types/Array_.php-J\-_+phpdocumentor-type-resolver/Types/Null_.phpJ\@%-phpdocumentor-type-resolver/Types/Object_.phpJ\Q,phpdocumentor-type-resolver/Types/Float_.phpJ\w,/phpdocumentor-type-resolver/Types/Resource_.phpJ\Nf -phpdocumentor-type-resolver/Types/Static_.phpUJ\Uޟ .phpdocumentor-type-resolver/Types/Nullable.phpJ\Ab+phpdocumentor-type-resolver/Types/Self_.phpJ\9'/phpdocumentor-type-resolver/Types/Iterable_.phpJ\OӶ-phpdocumentor-type-resolver/Types/Parent_.php4J\4j4phpdocumentor-type-resolver/Types/ContextFactory.phpJ\E3,phpdocumentor-type-resolver/Types/Mixed_.phpJ\ϯʶ-phpdocumentor-type-resolver/Types/Context.phpV J\V {.phpdocumentor-type-resolver/Types/Compound.php J\ |sebastian-version/Version.phpJ\N\Ƕsebastian-version/LICENSEJ\nwebmozart-assert/Assert.phpJ\ހwebmozart-assert/LICENSE<J\<t}php-file-iterator/Factory.phpJ\l Ҷphp-file-iterator/Iterator.php J\ %޶php-file-iterator/Facade.php J\ k*̶php-file-iterator/LICENSEJ\JU(%theseer-tokenizer/TokenCollection.phpr J\r g9+theseer-tokenizer/NamespaceUriException.phprJ\r+Atheseer-tokenizer/Exception.phpgJ\gլX"theseer-tokenizer/NamespaceUri.phpwJ\w 'U#theseer-tokenizer/XMLSerializer.php J\ E!{A.theseer-tokenizer/TokenCollectionException.phpuJ\uBKtheseer-tokenizer/Tokenizer.php3J\3atheseer-tokenizer/Token.phpqJ\q?theseer-tokenizer/LICENSEJ\R (0php-token-stream/Token/Stream/CachingFactory.phpJ\ \h!php-token-stream/Token/Stream.php>J\>php-token-stream/Token.php-aJ\-at̯php-token-stream/LICENSEJ\ }+phpdocumentor-reflection-common/Project.phpJ\/H ,phpdocumentor-reflection-common/Location.phpHJ\H?-+phpdocumentor-reflection-common/Element.php1J\1iUҶ)phpdocumentor-reflection-common/Fqsen.php J\ Ӝ'phpdocumentor-reflection-common/LICENSE9J\9*2Ȑ2phpdocumentor-reflection-common/ProjectFactory.phpJ\Q"ܶ(phpdocumentor-reflection-common/File.php7J\73"sebastian-diff/Differ.php9%J\9%D&sebastian-diff/Exception/Exception.php@J\@gն5sebastian-diff/Exception/InvalidArgumentException.phpJ\g3sebastian-diff/Exception/ConfigurationException.phpCJ\C< 1sebastian-diff/Diff.phpJ\3߬sebastian-diff/Parser.php J\ G24sebastian-diff/Output/AbstractChunkOutputBuilder.phpOJ\OEl4sebastian-diff/Output/DiffOutputBuilderInterface.php J\ zk2sebastian-diff/Output/UnifiedDiffOutputBuilder.php* J\* /sebastian-diff/Output/DiffOnlyOutputBuilder.phpJ\28>8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php(J\(Nsebastian-diff/Line.phpOJ\O' Bsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php J\ v<sebastian-diff/Chunk.phpmJ\mADsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.phpVJ\Vfն5sebastian-diff/LongestCommonSubsequenceCalculator.php<J\<sebastian-diff/LICENSE J\ Dte#"php-code-coverage/CodeCoverage.phpsJ\s php-code-coverage/Version.phpJ\php-code-coverage/Util.phpfJ\fQ9r'php-code-coverage/Node/AbstractNode.phpNJ\NH ʶ#php-code-coverage/Node/Iterator.php:J\:_c"php-code-coverage/Node/Builder.phpJ\a6Ҷ$php-code-coverage/Node/Directory.phpq$J\q$nr)öphp-code-coverage/Node/File.php6@J\6@dö(php-code-coverage/Report/Xml/Project.php= J\= \и'php-code-coverage/Report/Xml/Source.phpJ\v]'php-code-coverage/Report/Xml/Report.php J\ ۈ|%php-code-coverage/Report/Xml/Unit.php J\ mlض%php-code-coverage/Report/Xml/Node.phpfJ\faD*php-code-coverage/Report/Xml/Directory.phppJ\p h˶)php-code-coverage/Report/Xml/Coverage.phpJ\*'mc'php-code-coverage/Report/Xml/Method.phpJ\31&php-code-coverage/Report/Xml/Tests.phpJ\Ql'php-code-coverage/Report/Xml/Facade.php J\ )"'php-code-coverage/Report/Xml/Totals.php0J\0Zh1php-code-coverage/Report/Xml/BuildInformation.php J\ X%php-code-coverage/Report/Xml/File.phpJ\-ö*php-code-coverage/Report/Html/Renderer.php!J\!\x?4php-code-coverage/Report/Html/Renderer/Directory.php J\ c~4php-code-coverage/Report/Html/Renderer/Dashboard.phpp%J\p%)>php-code-coverage/Report/Html/Renderer/Template/file.html.dist J\ f "Hphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distAJ\AdsEphp-code-coverage/Report/Html/Renderer/Template/method_item.html.distJ\s:Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0J\0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svgJ\ZCphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distGJ\GlFphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'J\'O}Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.disttJ\t@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssXJ\X'#>php-code-coverage/Report/Html/Renderer/Template/css/custom.cssJ\Aphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%J\X%0,=php-code-coverage/Report/Html/Renderer/Template/css/style.cssJ\vEphp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.cssn`J\n`Cphp-code-coverage/Report/Html/Renderer/Template/directory.html.distJ\GMCphp-code-coverage/Report/Html/Renderer/Template/js/bootstrap.min.jsJ\"@php-code-coverage/Report/Html/Renderer/Template/js/jquery.min.jsSJ\S[`?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsRJ\RܶCphp-code-coverage/Exception/UnintentionallyCoveredCodeException.phpMJ\M))php-code-coverage/Exception/Exception.phpJ\~8php-code-coverage/Exception/InvalidArgumentException.phpJ\eUu@php-code-coverage/Exception/MissingCoversAnnotationException.phpJ\ʡ?php-code-coverage/Exception/CoveredCodeNotExecutedException.phpJ\X 0php-code-coverage/Exception/RuntimeException.phpJ\bPD#php-code-coverage/Driver/Xdebug.php J\ &" !php-code-coverage/Driver/PCOV.phplJ\ls,ݶ#php-code-coverage/Driver/Driver.phpJ\N#php-code-coverage/Driver/PHPDBG.php J\ dphp-code-coverage/LICENSEJ\IRphp-code-coverage/Filter.phpEJ\ER} phpunit.xsdAJ\ADF̶)phar-io-manifest/xml/ExtensionElement.phpBJ\B-phar-io-manifest/xml/ExtElementCollection.phpJ\#phar-io-manifest/xml/PhpElement.phpJ\p{:9phar-io-manifest/xml/ManifestDocumentLoadingException.phpJ\>)phar-io-manifest/xml/ManifestDocument.php J\ 1(phar-io-manifest/xml/RequiresElement.phpJ\(ͷ*phar-io-manifest/xml/ElementCollection.phpJ\'*(phar-io-manifest/xml/ManifestElement.php J\ :6X0phar-io-manifest/xml/AuthorElementCollection.php J\ Byζ&phar-io-manifest/xml/AuthorElement.php7J\73phar-io-manifest/xml/ComponentElementCollection.phpJ\#Iζ'phar-io-manifest/xml/BundlesElement.php&J\&Oϯ)phar-io-manifest/xml/ComponentElement.php>J\> +ȶ#phar-io-manifest/xml/ExtElement.phpJ\h/j3'phar-io-manifest/xml/LicenseElement.php4J\4 )phar-io-manifest/xml/CopyrightElement.phpJ\(phar-io-manifest/xml/ContainsElement.php'J\'Z,phar-io-manifest/values/BundledComponent.phpJ\-Ŷ0phar-io-manifest/values/CopyrightInformation.phpxJ\xtb6phar-io-manifest/values/BundledComponentCollection.php(J\( ڶ>phar-io-manifest/values/BundledComponentCollectionIterator.phpJ\}9phar-io-manifest/values/RequirementCollectionIterator.phpJ\n$phar-io-manifest/values/Manifest.php J\ D"phar-io-manifest/values/Author.php%J\%W8'phar-io-manifest/values/Requirement.phppJ\p6VA1phar-io-manifest/values/RequirementCollection.phpJ\)%phar-io-manifest/values/Extension.phpJ\0phar-io-manifest/values/Url.phpJ\f]1phar-io-manifest/values/PhpVersionRequirement.phpJ\,3phar-io-manifest/values/PhpExtensionRequirement.phpJ\<Ͷ phar-io-manifest/values/Type.phpJ\pn#phar-io-manifest/values/License.phpJ\*׶'phar-io-manifest/values/Application.phpJ\7$~׶4phar-io-manifest/values/AuthorCollectionIterator.phpaJ\a+phar-io-manifest/values/ApplicationName.phpcJ\c#phar-io-manifest/values/Library.phpJ\Fz!phar-io-manifest/values/Email.phpJ\cM,phar-io-manifest/values/AuthorCollection.phpJ\Gr7phar-io-manifest/exceptions/ManifestLoaderException.phpmJ\m8L:?phar-io-manifest/exceptions/InvalidApplicationNameException.phpJ\6KQ5phar-io-manifest/exceptions/InvalidEmailException.phpJ\3D?phar-io-manifest/exceptions/ManifestDocumentMapperException.php|J\|A )phar-io-manifest/exceptions/Exception.phpnJ\nY8phar-io-manifest/exceptions/ManifestElementException.phpuJ\uw/3phar-io-manifest/exceptions/InvalidUrlException.phpJ\)9phar-io-manifest/exceptions/ManifestDocumentException.phpvJ\v#phar-io-manifest/ManifestLoader.phpJ\B8 +phar-io-manifest/ManifestDocumentMapper.phpJ\@phar-io-manifest/LICENSEQJ\Q$W0'phar-io-manifest/ManifestSerializer.phpJ\t'phpunit/Util/TestDox/TestDoxPrinter.php+J\+$*phpunit/Util/TestDox/HtmlResultPrinter.php J\ w*phpunit/Util/TestDox/CliTestDoxPrinter.php*J\*Py'phpunit/Util/TestDox/NamePrettifier.phpQJ\Q؂#)phpunit/Util/TestDox/XmlResultPrinter.phpJ\[&phpunit/Util/TestDox/ResultPrinter.phpJ\kphpunit/Util/Filesystem.phpJ\~Fphpunit/Util/Printer.php J\ SDphpunit/Util/FileLoader.php- J\- zphpunit/Util/Color.phpJ\eƶphpunit/Util/Xml.phpJ\v,phpunit/Util/XdebugFilterScriptGenerator.phpJ\>Cphpunit/Util/Log/JUnit.php.J\.aphpunit/Util/Log/TeamCity.php*J\*:}ephpunit/Util/Json.php" J\" . y'phpunit/Util/PHP/AbstractPhpProcess.php&J\&6k&phpunit/Util/PHP/WindowsPhpProcess.phptJ\t2Kphpunit/Util/PHP/eval-stdin.php-J\--rX/phpunit/Util/PHP/Template/PhptTestCase.tpl.distbJ\b̶1phpunit/Util/PHP/Template/TestCaseMethod.tpl.distW J\W i0phpunit/Util/PHP/Template/TestCaseClass.tpl.dist J\ 3/Q&phpunit/Util/PHP/DefaultPhpProcess.phpJ\f¶phpunit/Util/Configuration.phpJ\Mv'phpunit/Util/ConfigurationGenerator.phpeJ\e&phpunit/Util/InvalidArgumentHelper.phpJ\fphpunit/Util/Type.phpJ\qphpunit/Util/Getopt.phpJ\gphpunit/Util/ErrorHandler.php3 J\3 DsҶ$phpunit/Util/XmlTestListRenderer.php% J\% `B%phpunit/Util/TextTestListRenderer.php@J\@7phpunit/Util/Blacklist.phpJ\Tphpunit/Util/Filter.phps J\s ]phpunit/Runner/Version.phpJ\G7!phpunit/Runner/BaseTestRunner.phpJ\"J)phpunit/Runner/DefaultTestResultCache.phpJ\8!phpunit/Runner/Filter/Factory.phpQJ\QW4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phpBJ\Bċ,phpunit/Runner/Filter/NameFilterIterator.phpl J\l ; h-phpunit/Runner/Filter/GroupFilterIterator.phpJ\/4phpunit/Runner/Filter/IncludeGroupFilterIterator.phpAJ\Aaphpunit/Runner/Exception.phpJ\m]'phpunit/Runner/ResultCacheExtension.php* J\* "phpunit/Runner/TestResultCache.phpJ\(i"phpunit/Runner/TestSuiteSorter.php.J\.nݶ,phpunit/Runner/Hook/AfterTestWarningHook.phpJ\ֆ,phpunit/Runner/Hook/AfterSkippedTestHook.phpJ\J phpunit/Runner/Hook/Hook.php+J\+kXM phpunit/Runner/Hook/TestHook.php<J\<xᤸ&phpunit/Runner/Hook/BeforeTestHook.phpJ\zݶ*phpunit/Runner/Hook/AfterTestErrorHook.phpJ\LXǶ+phpunit/Runner/Hook/TestListenerAdapter.phpJ\>P+phpunit/Runner/Hook/BeforeFirstTestHook.php{J\{.*phpunit/Runner/Hook/AfterRiskyTestHook.phpJ\jYM)phpunit/Runner/Hook/AfterLastTestHook.phpwJ\w~F%phpunit/Runner/Hook/AfterTestHook.phpQJ\Q,phpunit/Runner/Hook/AfterTestFailureHook.phpJ\zc/phpunit/Runner/Hook/AfterSuccessfulTestHook.phpJ\j3/phpunit/Runner/Hook/AfterIncompleteTestHook.phpJ\*K&phpunit/Runner/NullTestResultCache.phphJ\h6"phpunit/Runner/TestSuiteLoader.php8J\8+$˶*phpunit/Runner/StandardTestSuiteLoader.php J\ phpunit/Runner/PhptTestCase.phplQJ\lQ#~phpunit/Framework/TestSuite.phpnlJ\nlQ/phpunit/Framework/Test.phpJ\8 phpunit/Framework/TestResult.phpsJ\s̡4,%phpunit/Framework/SkippedTestCase.php J\ p)m<+phpunit/Framework/SkippedTestSuiteError.phpJ\f*phpunit/Framework/AssertionFailedError.phpjJ\jZY$phpunit/Framework/RiskyTestError.phpJ\ phpunit/Framework/Assert.phpԝJ\ԝV(phpunit/Framework/IncompleteTestCase.phpJ\ lp+phpunit/Framework/DataProviderTestSuite.phpJ\phpunit/Framework/MockObject/Matcher/ConsecutiveParameters.php J\ fr1phpunit/Framework/MockObject/InvocationMocker.phpCJ\C<phpunit/Framework/MockObject/Invocation/StaticInvocation.phpJ\ 諶<phpunit/Framework/MockObject/Invocation/ObjectInvocation.php1J\1H 6phpunit/Framework/MockObject/Invocation/Invocation.phpJ\=ݶ;phpunit/Framework/MockObject/Generator/deprecation.tpl.dist;J\;O5sCphpunit/Framework/MockObject/Generator/mocked_class_method.tpl.distJ\d>phpunit/Framework/MockObject/Generator/proxied_method.tpl.distJ\=hCphpunit/Framework/MockObject/Generator/proxied_method_void.tpl.distJ\|;phpunit/Framework/MockObject/Generator/trait_class.tpl.dist7J\7[$~<phpunit/Framework/MockObject/Generator/mocked_class.tpl.distAJ\ADK=phpunit/Framework/MockObject/Generator/mocked_method.tpl.dist]J\]~ӭ^;phpunit/Framework/MockObject/Generator/wsdl_method.tpl.dist<J\<iDphpunit/Framework/MockObject/Generator/mocked_static_method.tpl.distJ\wQ<phpunit/Framework/MockObject/Generator/mocked_clone.tpl.distJ\aT:phpunit/Framework/MockObject/Generator/wsdl_class.tpl.distJ\w&S>phpunit/Framework/MockObject/Generator/unmocked_clone.tpl.distJ\8W}ضBphpunit/Framework/MockObject/Generator/mocked_method_void.tpl.dist&J\&1phpunit/Framework/MockObject/Builder/Identity.phpJ\}"-phpunit/Framework/MockObject/Builder/Stub.phpJ\JT7phpunit/Framework/MockObject/Builder/NamespaceMatch.phpJ\ё.phpunit/Framework/MockObject/Builder/Match.phpJ\E8phpunit/Framework/MockObject/Builder/ParametersMatch.phpJ\9phpunit/Framework/MockObject/Builder/InvocationMocker.phpJ\Y8phpunit/Framework/MockObject/Builder/MethodNameMatch.phpCJ\C}k.phpunit/Framework/MockObject/MockMethodSet.phpJ\<)+phpunit/Framework/MockObject/MockMethod.php*J\*΄+phpunit/Framework/CodeCoverageException.phpJ\<߆]!phpunit/Framework/OutputError.phpJ\:tphpunit/Framework/Exception.php J\ ,t\c&phpunit/Framework/SkippedTestError.phpJ\=@$phpunit/Framework/SelfDescribing.phpJ\F.phpunit/Framework/PHPTAssertionFailedError.php J\ >G$phpunit/Framework/IncompleteTest.phpJ\#9dphpunit/Framework/TestCase.phpJ\w!phpunit/Framework/SkippedTest.phpJ\)ً5phpunit/Framework/UnintentionallyCoveredCodeError.phpJ\it)phpunit/Framework/IncompleteTestError.phpJ\)R6phpunit/Framework/MissingCoversAnnotationException.phpJ\1ȶ&phpunit/Framework/ExceptionWrapper.php J\ {Fj+phpunit/Framework/SyntheticSkippedError.phpJ\ӏ7phpunit/Framework/TestListenerDefaultImplementation.phpJ\ql+phpunit/Framework/Constraint/LogicalXor.php@ J\@ 3n2phpunit/Framework/Constraint/RegularExpression.php5J\5FgC(phpunit/Framework/Constraint/IsEqual.phpJ\zζ@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php@J\@ +phpunit/Framework/Constraint/Constraint.phpJ\e+phpunit/Framework/Constraint/IsInfinite.php>J\>/phpunit/Framework/Constraint/StringContains.phpJ\N0T*phpunit/Framework/Constraint/LogicalOr.php J\ "0l)phpunit/Framework/Constraint/Callback.phpJ\,phpunit/Framework/Constraint/JsonMatches.phpl J\l fJ'phpunit/Framework/Constraint/IsTrue.php-J\-2Đ+phpunit/Framework/Constraint/LogicalNot.phpJ\?ʶ.phpunit/Framework/Constraint/ExceptionCode.php;J\;-phpunit/Framework/Constraint/IsInstanceOf.phpJ\ ]8'phpunit/Framework/Constraint/IsJson.php J\ g,phpunit/Framework/Constraint/ArrayHasKey.phpJ\~y/phpunit/Framework/Constraint/StringEndsWith.phpOJ\O ,phpunit/Framework/Constraint/ArraySubset.phpJ\k'phpunit/Framework/Constraint/IsNull.php-J\-ɭw*phpunit/Framework/Constraint/Exception.phpJ\v 7,phpunit/Framework/Constraint/GreaterThan.phpJ\Ƕ)phpunit/Framework/Constraint/LessThan.phpJ\ 4+phpunit/Framework/Constraint/FileExists.php[J\[zEPe)phpunit/Framework/Constraint/IsFinite.php6J\6{0jR,phpunit/Framework/Constraint/IsIdentical.phpJ\㯳c(phpunit/Framework/Constraint/IsEmpty.phpJ\-8phpunit/Framework/Constraint/TraversableContainsOnly.phpJ\{( /1phpunit/Framework/Constraint/ExceptionMessage.php-J\-L88phpunit/Framework/Constraint/ClassHasStaticAttribute.phpJ\P+phpunit/Framework/Constraint/IsWritable.phpdJ\d_P*phpunit/Framework/Constraint/Attribute.phpJ\1F3phpunit/Framework/Constraint/ObjectHasAttribute.phpbJ\bt`0phpunit/Framework/Constraint/DirectoryExists.phpjJ\j޶?phpunit/Framework/Constraint/StringMatchesFormatDescription.phpe J\e nG*phpunit/Framework/Constraint/Composite.phpNJ\N7(phpunit/Framework/Constraint/IsFalse.php1J\1+phpunit/Framework/Constraint/LogicalAnd.phpY J\Y 2/Bphpunit/Framework/Constraint/ExceptionMessageRegularExpression.php]J\] &phpunit/Framework/Constraint/Count.php J\ -sebastian-code-unit-reverse-lookup/Wizard.phpe J\e *sebastian-code-unit-reverse-lookup/LICENSEJ\XX * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Exporter; use SebastianBergmann\RecursionContext\Context; /** * A nifty utility for visualizing PHP variables. * * * export(new Exception); * */ class Exporter { /** * Exports a value as 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 * @param int $indentation The indentation level of the 2nd+ line * * @return string */ public function export($value, $indentation = 0) { return $this->recursiveExport($value, $indentation); } /** * @param mixed $data * @param Context $context * * @return string */ public function shortenedRecursiveExport(&$data, Context $context = null) { $result = []; $exporter = new self(); if (!$context) { $context = new Context; } $array = $data; $context->add($data); foreach ($array as $key => $value) { if (is_array($value)) { if ($context->contains($data[$key]) !== false) { $result[] = '*RECURSION*'; } else { $result[] = sprintf( 'array(%s)', $this->shortenedRecursiveExport($data[$key], $context) ); } } else { $result[] = $exporter->shortenedExport($value); } } return implode(', ', $result); } /** * Exports a value into a single-line string * * The output of this method is similar to the output of * SebastianBergmann\Exporter\Exporter::export(). * * Newlines are replaced by the visible string '\n'. * Contents of arrays and objects (if any) are replaced by '...'. * * @param mixed $value * * @return string * * @see SebastianBergmann\Exporter\Exporter::export */ public function shortenedExport($value) { if (is_string($value)) { $string = str_replace("\n", '', $this->export($value)); if (function_exists('mb_strlen')) { if (mb_strlen($string) > 40) { $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); } } else { if (strlen($string) > 40) { $string = substr($string, 0, 30) . '...' . substr($string, -7); } } return $string; } if (is_object($value)) { return sprintf( '%s Object (%s)', get_class($value), count($this->toArray($value)) > 0 ? '...' : '' ); } if (is_array($value)) { return sprintf( 'Array (%s)', count($value) > 0 ? '...' : '' ); } return $this->export($value); } /** * Converts an object to an array containing all of its private, protected * and public properties. * * @param mixed $value * * @return array */ public function toArray($value) { if (!is_object($value)) { return (array) $value; } $array = []; foreach ((array) $value as $key => $val) { // 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]; } // See https://github.com/php/php-src/commit/5721132 if ($key === "\0gcdata") { continue; } $array[$key] = $val; } // Some internal classes like SplObjectStorage don't work with the // above (fast) mechanism nor with reflection in Zend. // Format the output similarly to print_r() in this case if ($value instanceof \SplObjectStorage) { // However, the fast method does work in HHVM, and exposes the // internal implementation. Hide it again. if (property_exists('\SplObjectStorage', '__storage')) { unset($array['__storage']); } elseif (property_exists('\SplObjectStorage', 'storage')) { unset($array['storage']); } if (property_exists('\SplObjectStorage', '__key')) { unset($array['__key']); } foreach ($value as $key => $val) { $array[spl_object_hash($val)] = [ 'obj' => $val, 'inf' => $value->getInfo(), ]; } } return $array; } /** * Recursive implementation of export * * @param mixed $value The value to export * @param int $indentation The indentation level of the 2nd+ line * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects * * @return string * * @see SebastianBergmann\Exporter\Exporter::export */ protected function recursiveExport(&$value, $indentation, $processed = null) { if ($value === null) { return 'null'; } if ($value === true) { return 'true'; } if ($value === false) { return 'false'; } if (is_float($value) && floatval(intval($value)) === $value) { return "$value.0"; } if (is_resource($value)) { return sprintf( 'resource(%d) of type (%s)', $value, get_resource_type($value) ); } if (is_string($value)) { // Match for most non printable chars somewhat taking multibyte chars into account if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) { return 'Binary String: 0x' . bin2hex($value); } return "'" . str_replace('', "\n", str_replace( ["\r\n", "\n\r", "\r", "\n"], ['\r\n', '\n\r', '\r', '\n'], $value ) ) . "'"; } $whitespace = str_repeat(' ', 4 * $indentation); if (!$processed) { $processed = new Context; } if (is_array($value)) { if (($key = $processed->contains($value)) !== false) { return 'Array &' . $key; } $array = $value; $key = $processed->add($value); $values = ''; if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($value[$k], $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('Array &%s (%s)', $key, $values); } if (is_object($value)) { $class = get_class($value); if ($hash = $processed->contains($value)) { return sprintf('%s Object &%s', $class, $hash); } $hash = $processed->add($value); $values = ''; $array = $this->toArray($value); if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($v, $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('%s Object &%s (%s)', $class, $hash, $values); } return var_export($value, true); } } Exporter Copyright (c) 2002-2017, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Comparator; use SebastianBergmann\Comparator\Factory as BaseFactory; /** * Prophecy comparator factory. * * @author Konstantin Kudryashov */ final class Factory extends BaseFactory { /** * @var Factory */ private static $instance; public function __construct() { parent::__construct(); $this->register(new ClosureComparator()); $this->register(new ProphecyComparator()); } /** * @return Factory */ public static function getInstance() { if (self::$instance === null) { self::$instance = new Factory; } return self::$instance; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Comparator; use Prophecy\Prophecy\ProphecyInterface; use SebastianBergmann\Comparator\ObjectComparator; class ProphecyComparator extends ObjectComparator { public function accepts($expected, $actual) { return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface; } public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) { parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Comparator; use SebastianBergmann\Comparator\Comparator; use SebastianBergmann\Comparator\ComparisonFailure; /** * Closure comparator. * * @author Konstantin Kudryashov */ final class ClosureComparator extends Comparator { public function accepts($expected, $actual) { return is_object($expected) && $expected instanceof \Closure && is_object($actual) && $actual instanceof \Closure; } public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) { throw new ComparisonFailure( $expected, $actual, // we don't need a diff '', '', false, 'all closures are born different' ); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Call; use Prophecy\Exception\Prophecy\MethodProphecyException; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Argument\ArgumentsWildcard; use Prophecy\Util\StringUtil; use Prophecy\Exception\Call\UnexpectedCallException; /** * Calls receiver & manager. * * @author Konstantin Kudryashov */ class CallCenter { private $util; /** * @var Call[] */ private $recordedCalls = array(); /** * Initializes call center. * * @param StringUtil $util */ public function __construct(StringUtil $util = null) { $this->util = $util ?: new StringUtil; } /** * Makes and records specific method call for object prophecy. * * @param ObjectProphecy $prophecy * @param string $methodName * @param array $arguments * * @return mixed Returns null if no promise for prophecy found or promise return value. * * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found */ public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) { // For efficiency exclude 'args' from the generated backtrace if (PHP_VERSION_ID >= 50400) { // Limit backtrace to last 3 calls as we don't use the rest // Limit argument was introduced in PHP 5.4.0 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $backtrace = debug_backtrace(); } $file = $line = null; if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { $file = $backtrace[2]['file']; $line = $backtrace[2]['line']; } // If no method prophecies defined, then it's a dummy, so we'll just return null if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) { $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); return null; } // There are method prophecies, so it's a fake/stub. Searching prophecy for this call $matches = array(); foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { $matches[] = array($score, $methodProphecy); } } // If fake/stub doesn't have method prophecy for this call - throw exception if (!count($matches)) { throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments); } // Sort matches by their score value @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); $score = $matches[0][0]; // If Highest rated method prophecy has a promise - execute it or return null instead $methodProphecy = $matches[0][1]; $returnValue = null; $exception = null; if ($promise = $methodProphecy->getPromise()) { try { $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); } catch (\Exception $e) { $exception = $e; } } if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { throw new MethodProphecyException( "The method \"$methodName\" has a void return type, but the promise returned a value", $methodProphecy ); } $this->recordedCalls[] = $call = new Call( $methodName, $arguments, $returnValue, $exception, $file, $line ); $call->addScore($methodProphecy->getArgumentsWildcard(), $score); if (null !== $exception) { throw $exception; } return $returnValue; } /** * Searches for calls by method name & arguments wildcard. * * @param string $methodName * @param ArgumentsWildcard $wildcard * * @return Call[] */ public function findCalls($methodName, ArgumentsWildcard $wildcard) { return array_values( array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { return $methodName === $call->getMethodName() && 0 < $call->getScore($wildcard) ; }) ); } private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, array $arguments) { $classname = get_class($prophecy->reveal()); $indentationLength = 8; // looks good $argstring = implode( ",\n", $this->indentArguments( array_map(array($this->util, 'stringify'), $arguments), $indentationLength ) ); $expected = array(); foreach (call_user_func_array('array_merge', $prophecy->getMethodProphecies()) as $methodProphecy) { $expected[] = sprintf( " - %s(\n" . "%s\n" . " )", $methodProphecy->getMethodName(), implode( ",\n", $this->indentArguments( array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), $indentationLength ) ) ); } return new UnexpectedCallException( sprintf( "Unexpected method call on %s:\n". " - %s(\n". "%s\n". " )\n". "expected calls were:\n". "%s", $classname, $methodName, $argstring, implode("\n", $expected) ), $prophecy, $methodName, $arguments ); } private function formatExceptionMessage(MethodProphecy $methodProphecy) { return sprintf( " - %s(\n". "%s\n". " )", $methodProphecy->getMethodName(), implode( ",\n", $this->indentArguments( array_map( function ($token) { return (string) $token; }, $methodProphecy->getArgumentsWildcard()->getTokens() ), $indentationLength ) ) ); } private function indentArguments(array $arguments, $indentationLength) { return preg_replace_callback( '/^/m', function () use ($indentationLength) { return str_repeat(' ', $indentationLength); }, $arguments ); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Call; use Exception; use Prophecy\Argument\ArgumentsWildcard; /** * Call object. * * @author Konstantin Kudryashov */ class Call { private $methodName; private $arguments; private $returnValue; private $exception; private $file; private $line; private $scores; /** * Initializes call. * * @param string $methodName * @param array $arguments * @param mixed $returnValue * @param Exception $exception * @param null|string $file * @param null|int $line */ public function __construct($methodName, array $arguments, $returnValue, Exception $exception = null, $file, $line) { $this->methodName = $methodName; $this->arguments = $arguments; $this->returnValue = $returnValue; $this->exception = $exception; $this->scores = new \SplObjectStorage(); if ($file) { $this->file = $file; $this->line = intval($line); } } /** * Returns called method name. * * @return string */ public function getMethodName() { return $this->methodName; } /** * Returns called method arguments. * * @return array */ public function getArguments() { return $this->arguments; } /** * Returns called method return value. * * @return null|mixed */ public function getReturnValue() { return $this->returnValue; } /** * Returns exception that call thrown. * * @return null|Exception */ public function getException() { return $this->exception; } /** * Returns callee filename. * * @return string */ public function getFile() { return $this->file; } /** * Returns callee line number. * * @return int */ public function getLine() { return $this->line; } /** * Returns short notation for callee place. * * @return string */ public function getCallPlace() { if (null === $this->file) { return 'unknown'; } return sprintf('%s:%d', $this->file, $this->line); } /** * Adds the wildcard match score for the provided wildcard. * * @param ArgumentsWildcard $wildcard * @param false|int $score * * @return $this */ public function addScore(ArgumentsWildcard $wildcard, $score) { $this->scores[$wildcard] = $score; return $this; } /** * Returns wildcard match score for the provided wildcard. The score is * calculated if not already done. * * @param ArgumentsWildcard $wildcard * * @return false|int False OR integer score (higher - better) */ public function getScore(ArgumentsWildcard $wildcard) { if (isset($this->scores[$wildcard])) { return $this->scores[$wildcard]; } return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Any single value token. * * @author Konstantin Kudryashov */ class AnyValueToken implements TokenInterface { /** * Always scores 3 for any argument. * * @param $argument * * @return int */ public function scoreArgument($argument) { return 3; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return '*'; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Logical NOT token. * * @author Boris Mikhaylov */ class LogicalNotToken implements TokenInterface { /** @var \Prophecy\Argument\Token\TokenInterface */ private $token; /** * @param mixed $value exact value or token */ public function __construct($value) { $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value); } /** * Scores 4 when preset token does not match the argument. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { return false === $this->token->scoreArgument($argument) ? 4 : false; } /** * Returns true if preset token is last. * * @return bool|int */ public function isLast() { return $this->token->isLast(); } /** * Returns originating token. * * @return TokenInterface */ public function getOriginatingToken() { return $this->token; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('not(%s)', $this->token); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Array every entry token. * * @author Adrien Brault */ class ArrayEveryEntryToken implements TokenInterface { /** * @var TokenInterface */ private $value; /** * @param mixed $value exact value or token */ public function __construct($value) { if (!$value instanceof TokenInterface) { $value = new ExactValueToken($value); } $this->value = $value; } /** * {@inheritdoc} */ public function scoreArgument($argument) { if (!$argument instanceof \Traversable && !is_array($argument)) { return false; } $scores = array(); foreach ($argument as $key => $argumentEntry) { $scores[] = $this->value->scoreArgument($argumentEntry); } if (empty($scores) || in_array(false, $scores, true)) { return false; } return array_sum($scores) / count($scores); } /** * {@inheritdoc} */ public function isLast() { return false; } /** * {@inheritdoc} */ public function __toString() { return sprintf('[%s, ..., %s]', $this->value, $this->value); } /** * @return TokenInterface */ public function getValue() { return $this->value; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Logical AND token. * * @author Boris Mikhaylov */ class LogicalAndToken implements TokenInterface { private $tokens = array(); /** * @param array $arguments exact values or tokens */ public function __construct(array $arguments) { foreach ($arguments as $argument) { if (!$argument instanceof TokenInterface) { $argument = new ExactValueToken($argument); } $this->tokens[] = $argument; } } /** * Scores maximum score from scores returned by tokens for this argument if all of them score. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { if (0 === count($this->tokens)) { return false; } $maxScore = 0; foreach ($this->tokens as $token) { $score = $token->scoreArgument($argument); if (false === $score) { return false; } $maxScore = max($score, $maxScore); } return $maxScore; } /** * Returns false. * * @return boolean */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('bool(%s)', implode(' AND ', $this->tokens)); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Approximate value token * * @author Daniel Leech */ class ApproximateValueToken implements TokenInterface { private $value; private $precision; public function __construct($value, $precision = 0) { $this->value = $value; $this->precision = $precision; } /** * {@inheritdoc} */ public function scoreArgument($argument) { return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false; } /** * {@inheritdoc} */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('≅%s', round($this->value, $this->precision)); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Any values token. * * @author Konstantin Kudryashov */ class AnyValuesToken implements TokenInterface { /** * Always scores 2 for any argument. * * @param $argument * * @return int */ public function scoreArgument($argument) { return 2; } /** * Returns true to stop wildcard from processing other tokens. * * @return bool */ public function isLast() { return true; } /** * Returns string representation for token. * * @return string */ public function __toString() { return '* [, ...]'; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Argument token interface. * * @author Konstantin Kudryashov */ interface TokenInterface { /** * Calculates token match score for provided argument. * * @param $argument * * @return bool|int */ public function scoreArgument($argument); /** * Returns true if this token prevents check of other tokens (is last one). * * @return bool|int */ public function isLast(); /** * Returns string representation for token. * * @return string */ public function __toString(); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * String contains token. * * @author Peter Mitchell */ class StringContainsToken implements TokenInterface { private $value; /** * Initializes token. * * @param string $value */ public function __construct($value) { $this->value = $value; } public function scoreArgument($argument) { return is_string($argument) && strpos($argument, $this->value) !== false ? 6 : false; } /** * Returns preset value against which token checks arguments. * * @return mixed */ public function getValue() { return $this->value; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('contains("%s")', $this->value); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; use Prophecy\Util\StringUtil; /** * Identical value token. * * @author Florian Voutzinos */ class IdenticalValueToken implements TokenInterface { private $value; private $string; private $util; /** * Initializes token. * * @param mixed $value * @param StringUtil $util */ public function __construct($value, StringUtil $util = null) { $this->value = $value; $this->util = $util ?: new StringUtil(); } /** * Scores 11 if argument matches preset value. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { return $argument === $this->value ? 11 : false; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { if (null === $this->string) { $this->string = sprintf('identical(%s)', $this->util->stringify($this->value)); } return $this->string; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; use Prophecy\Exception\InvalidArgumentException; /** * Value type token. * * @author Konstantin Kudryashov */ class TypeToken implements TokenInterface { private $type; /** * @param string $type */ public function __construct($type) { $checker = "is_{$type}"; if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) { throw new InvalidArgumentException(sprintf( 'Type or class name expected as an argument to TypeToken, but got %s.', $type )); } $this->type = $type; } /** * Scores 5 if argument has the same type this token was constructed with. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { $checker = "is_{$this->type}"; if (function_exists($checker)) { return call_user_func($checker, $argument) ? 5 : false; } return $argument instanceof $this->type ? 5 : false; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('type(%s)', $this->type); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; use Prophecy\Exception\InvalidArgumentException; /** * Callback-verified token. * * @author Konstantin Kudryashov */ class CallbackToken implements TokenInterface { private $callback; /** * Initializes token. * * @param callable $callback * * @throws \Prophecy\Exception\InvalidArgumentException */ public function __construct($callback) { if (!is_callable($callback)) { throw new InvalidArgumentException(sprintf( 'Callable expected as an argument to CallbackToken, but got %s.', gettype($callback) )); } $this->callback = $callback; } /** * Scores 7 if callback returns true, false otherwise. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { return call_user_func($this->callback, $argument) ? 7 : false; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return 'callback()'; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; use SebastianBergmann\Comparator\ComparisonFailure; use Prophecy\Comparator\Factory as ComparatorFactory; use Prophecy\Util\StringUtil; /** * Object state-checker token. * * @author Konstantin Kudryashov */ class ObjectStateToken implements TokenInterface { private $name; private $value; private $util; private $comparatorFactory; /** * Initializes token. * * @param string $methodName * @param mixed $value Expected return value * @param null|StringUtil $util * @param ComparatorFactory $comparatorFactory */ public function __construct( $methodName, $value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null ) { $this->name = $methodName; $this->value = $value; $this->util = $util ?: new StringUtil; $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } /** * Scores 8 if argument is an object, which method returns expected value. * * @param mixed $argument * * @return bool|int */ public function scoreArgument($argument) { if (is_object($argument) && method_exists($argument, $this->name)) { $actual = call_user_func(array($argument, $this->name)); $comparator = $this->comparatorFactory->getComparatorFor( $this->value, $actual ); try { $comparator->assertEquals($this->value, $actual); return 8; } catch (ComparisonFailure $failure) { return false; } } if (is_object($argument) && property_exists($argument, $this->name)) { return $argument->{$this->name} === $this->value ? 8 : false; } return false; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('state(%s(), %s)', $this->name, $this->util->stringify($this->value) ); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; use SebastianBergmann\Comparator\ComparisonFailure; use Prophecy\Comparator\Factory as ComparatorFactory; use Prophecy\Util\StringUtil; /** * Exact value token. * * @author Konstantin Kudryashov */ class ExactValueToken implements TokenInterface { private $value; private $string; private $util; private $comparatorFactory; /** * Initializes token. * * @param mixed $value * @param StringUtil $util * @param ComparatorFactory $comparatorFactory */ public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) { $this->value = $value; $this->util = $util ?: new StringUtil(); $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } /** * Scores 10 if argument matches preset value. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { if (is_object($argument) && is_object($this->value)) { $comparator = $this->comparatorFactory->getComparatorFor( $argument, $this->value ); try { $comparator->assertEquals($argument, $this->value); return 10; } catch (ComparisonFailure $failure) {} } // If either one is an object it should be castable to a string if (is_object($argument) xor is_object($this->value)) { if (is_object($argument) && !method_exists($argument, '__toString')) { return false; } if (is_object($this->value) && !method_exists($this->value, '__toString')) { return false; } } elseif (is_numeric($argument) && is_numeric($this->value)) { // noop } elseif (gettype($argument) !== gettype($this->value)) { return false; } return $argument == $this->value ? 10 : false; } /** * Returns preset value against which token checks arguments. * * @return mixed */ public function getValue() { return $this->value; } /** * Returns false. * * @return bool */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { if (null === $this->string) { $this->string = sprintf('exact(%s)', $this->util->stringify($this->value)); } return $this->string; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; /** * Array elements count token. * * @author Boris Mikhaylov */ class ArrayCountToken implements TokenInterface { private $count; /** * @param integer $value */ public function __construct($value) { $this->count = $value; } /** * Scores 6 when argument has preset number of elements. * * @param $argument * * @return bool|int */ public function scoreArgument($argument) { return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false; } /** * Returns false. * * @return boolean */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('count(%s)', $this->count); } /** * Returns true if object is either array or instance of \Countable * * @param $argument * @return bool */ private function isCountable($argument) { return (is_array($argument) || $argument instanceof \Countable); } /** * Returns true if $argument has expected number of elements * * @param array|\Countable $argument * * @return bool */ private function hasProperCount($argument) { return $this->count === count($argument); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument\Token; use Prophecy\Exception\InvalidArgumentException; /** * Array entry token. * * @author Boris Mikhaylov */ class ArrayEntryToken implements TokenInterface { /** @var \Prophecy\Argument\Token\TokenInterface */ private $key; /** @var \Prophecy\Argument\Token\TokenInterface */ private $value; /** * @param mixed $key exact value or token * @param mixed $value exact value or token */ public function __construct($key, $value) { $this->key = $this->wrapIntoExactValueToken($key); $this->value = $this->wrapIntoExactValueToken($value); } /** * Scores half of combined scores from key and value tokens for same entry. Capped at 8. * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. * * @param array|\ArrayAccess|\Traversable $argument * * @throws \Prophecy\Exception\InvalidArgumentException * @return bool|int */ public function scoreArgument($argument) { if ($argument instanceof \Traversable) { $argument = iterator_to_array($argument); } if ($argument instanceof \ArrayAccess) { $argument = $this->convertArrayAccessToEntry($argument); } if (!is_array($argument) || empty($argument)) { return false; } $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument)); $valueScores = array_map(array($this->value,'scoreArgument'), $argument); $scoreEntry = function ($value, $key) { return $value && $key ? min(8, ($key + $value) / 2) : false; }; return max(array_map($scoreEntry, $valueScores, $keyScores)); } /** * Returns false. * * @return boolean */ public function isLast() { return false; } /** * Returns string representation for token. * * @return string */ public function __toString() { return sprintf('[..., %s => %s, ...]', $this->key, $this->value); } /** * Returns key * * @return TokenInterface */ public function getKey() { return $this->key; } /** * Returns value * * @return TokenInterface */ public function getValue() { return $this->value; } /** * Wraps non token $value into ExactValueToken * * @param $value * @return TokenInterface */ private function wrapIntoExactValueToken($value) { return $value instanceof TokenInterface ? $value : new ExactValueToken($value); } /** * Converts instance of \ArrayAccess to key => value array entry * * @param \ArrayAccess $object * * @return array|null * @throws \Prophecy\Exception\InvalidArgumentException */ private function convertArrayAccessToEntry(\ArrayAccess $object) { if (!$this->key instanceof ExactValueToken) { throw new InvalidArgumentException(sprintf( 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL. 'But you used `%s`.', $this->key )); } $key = $this->key->getValue(); return $object->offsetExists($key) ? array($key => $object[$key]) : array(); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Argument; /** * Arguments wildcarding. * * @author Konstantin Kudryashov */ class ArgumentsWildcard { /** * @var Token\TokenInterface[] */ private $tokens = array(); private $string; /** * Initializes wildcard. * * @param array $arguments Array of argument tokens or values */ public function __construct(array $arguments) { foreach ($arguments as $argument) { if (!$argument instanceof Token\TokenInterface) { $argument = new Token\ExactValueToken($argument); } $this->tokens[] = $argument; } } /** * Calculates wildcard match score for provided arguments. * * @param array $arguments * * @return false|int False OR integer score (higher - better) */ public function scoreArguments(array $arguments) { if (0 == count($arguments) && 0 == count($this->tokens)) { return 1; } $arguments = array_values($arguments); $totalScore = 0; foreach ($this->tokens as $i => $token) { $argument = isset($arguments[$i]) ? $arguments[$i] : null; if (1 >= $score = $token->scoreArgument($argument)) { return false; } $totalScore += $score; if (true === $token->isLast()) { return $totalScore; } } if (count($arguments) > count($this->tokens)) { return false; } return $totalScore; } /** * Returns string representation for wildcard. * * @return string */ public function __toString() { if (null === $this->string) { $this->string = implode(', ', array_map(function ($token) { return (string) $token; }, $this->tokens)); } return $this->string; } /** * @return array */ public function getTokens() { return $this->tokens; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Promise; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Exception\InvalidArgumentException; use Closure; /** * Callback promise. * * @author Konstantin Kudryashov */ class CallbackPromise implements PromiseInterface { private $callback; /** * Initializes callback promise. * * @param callable $callback Custom callback * * @throws \Prophecy\Exception\InvalidArgumentException */ public function __construct($callback) { if (!is_callable($callback)) { throw new InvalidArgumentException(sprintf( 'Callable expected as an argument to CallbackPromise, but got %s.', gettype($callback) )); } $this->callback = $callback; } /** * Evaluates promise callback. * * @param array $args * @param ObjectProphecy $object * @param MethodProphecy $method * * @return mixed */ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { $callback = $this->callback; if ($callback instanceof Closure && method_exists('Closure', 'bind')) { $callback = Closure::bind($callback, $object); } return call_user_func($callback, $args, $object, $method); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Promise; use Prophecy\Exception\InvalidArgumentException; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; /** * Return argument promise. * * @author Konstantin Kudryashov */ class ReturnArgumentPromise implements PromiseInterface { /** * @var int */ private $index; /** * Initializes callback promise. * * @param int $index The zero-indexed number of the argument to return * * @throws \Prophecy\Exception\InvalidArgumentException */ public function __construct($index = 0) { if (!is_int($index) || $index < 0) { throw new InvalidArgumentException(sprintf( 'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', $index )); } $this->index = $index; } /** * Returns nth argument if has one, null otherwise. * * @param array $args * @param ObjectProphecy $object * @param MethodProphecy $method * * @return null|mixed */ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { return count($args) > $this->index ? $args[$this->index] : null; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Promise; use Doctrine\Instantiator\Instantiator; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Exception\InvalidArgumentException; use ReflectionClass; /** * Throw promise. * * @author Konstantin Kudryashov */ class ThrowPromise implements PromiseInterface { private $exception; /** * @var \Doctrine\Instantiator\Instantiator */ private $instantiator; /** * Initializes promise. * * @param string|\Exception|\Throwable $exception Exception class name or instance * * @throws \Prophecy\Exception\InvalidArgumentException */ public function __construct($exception) { if (is_string($exception)) { if (!class_exists($exception) || !$this->isAValidThrowable($exception)) { throw new InvalidArgumentException(sprintf( 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', $exception )); } } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { throw new InvalidArgumentException(sprintf( 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', is_object($exception) ? get_class($exception) : gettype($exception) )); } $this->exception = $exception; } /** * Throws predefined exception. * * @param array $args * @param ObjectProphecy $object * @param MethodProphecy $method * * @throws object */ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { if (is_string($this->exception)) { $classname = $this->exception; $reflection = new ReflectionClass($classname); $constructor = $reflection->getConstructor(); if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { throw $reflection->newInstance(); } if (!$this->instantiator) { $this->instantiator = new Instantiator(); } throw $this->instantiator->instantiate($classname); } throw $this->exception; } /** * @param string $exception * * @return bool */ private function isAValidThrowable($exception) { return is_a($exception, 'Exception', true) || is_subclass_of($exception, 'Throwable', true); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Promise; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; /** * Promise interface. * Promises are logical blocks, tied to `will...` keyword. * * @author Konstantin Kudryashov */ interface PromiseInterface { /** * Evaluates promise. * * @param array $args * @param ObjectProphecy $object * @param MethodProphecy $method * * @return mixed */ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Promise; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; /** * Return promise. * * @author Konstantin Kudryashov */ class ReturnPromise implements PromiseInterface { private $returnValues = array(); /** * Initializes promise. * * @param array $returnValues Array of values */ public function __construct(array $returnValues) { $this->returnValues = $returnValues; } /** * Returns saved values one by one until last one, then continuously returns last value. * * @param array $args * @param ObjectProphecy $object * @param MethodProphecy $method * * @return mixed */ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { $value = array_shift($this->returnValues); if (!count($this->returnValues)) { $this->returnValues[] = $value; } return $value; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prophecy; use Prophecy\Argument; use Prophecy\Prophet; use Prophecy\Promise; use Prophecy\Prediction; use Prophecy\Exception\Doubler\MethodNotFoundException; use Prophecy\Exception\InvalidArgumentException; use Prophecy\Exception\Prophecy\MethodProphecyException; /** * Method prophecy. * * @author Konstantin Kudryashov */ class MethodProphecy { private $objectProphecy; private $methodName; private $argumentsWildcard; private $promise; private $prediction; private $checkedPredictions = array(); private $bound = false; private $voidReturnType = false; /** * Initializes method prophecy. * * @param ObjectProphecy $objectProphecy * @param string $methodName * @param null|Argument\ArgumentsWildcard|array $arguments * * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found */ public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null) { $double = $objectProphecy->reveal(); if (!method_exists($double, $methodName)) { throw new MethodNotFoundException(sprintf( 'Method `%s::%s()` is not defined.', get_class($double), $methodName ), get_class($double), $methodName, $arguments); } $this->objectProphecy = $objectProphecy; $this->methodName = $methodName; $reflectedMethod = new \ReflectionMethod($double, $methodName); if ($reflectedMethod->isFinal()) { throw new MethodProphecyException(sprintf( "Can not add prophecy for a method `%s::%s()`\n". "as it is a final method.", get_class($double), $methodName ), $this); } if (null !== $arguments) { $this->withArguments($arguments); } if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) { $type = (string) $reflectedMethod->getReturnType(); if ('void' === $type) { $this->voidReturnType = true; } $this->will(function () use ($type) { switch ($type) { case 'void': return; case 'string': return ''; case 'float': return 0.0; case 'int': return 0; case 'bool': return false; case 'array': return array(); case 'callable': case 'Closure': return function () {}; case 'Traversable': case 'Generator': // Remove eval() when minimum version >=5.5 /** @var callable $generator */ $generator = eval('return function () { yield; };'); return $generator(); default: $prophet = new Prophet; return $prophet->prophesize($type)->reveal(); } }); } } /** * Sets argument wildcard. * * @param array|Argument\ArgumentsWildcard $arguments * * @return $this * * @throws \Prophecy\Exception\InvalidArgumentException */ public function withArguments($arguments) { if (is_array($arguments)) { $arguments = new Argument\ArgumentsWildcard($arguments); } if (!$arguments instanceof Argument\ArgumentsWildcard) { throw new InvalidArgumentException(sprintf( "Either an array or an instance of ArgumentsWildcard expected as\n". 'a `MethodProphecy::withArguments()` argument, but got %s.', gettype($arguments) )); } $this->argumentsWildcard = $arguments; return $this; } /** * Sets custom promise to the prophecy. * * @param callable|Promise\PromiseInterface $promise * * @return $this * * @throws \Prophecy\Exception\InvalidArgumentException */ public function will($promise) { if (is_callable($promise)) { $promise = new Promise\CallbackPromise($promise); } if (!$promise instanceof Promise\PromiseInterface) { throw new InvalidArgumentException(sprintf( 'Expected callable or instance of PromiseInterface, but got %s.', gettype($promise) )); } $this->bindToObjectProphecy(); $this->promise = $promise; return $this; } /** * Sets return promise to the prophecy. * * @see \Prophecy\Promise\ReturnPromise * * @return $this */ public function willReturn() { if ($this->voidReturnType) { throw new MethodProphecyException( "The method \"$this->methodName\" has a void return type, and so cannot return anything", $this ); } return $this->will(new Promise\ReturnPromise(func_get_args())); } /** * Sets return argument promise to the prophecy. * * @param int $index The zero-indexed number of the argument to return * * @see \Prophecy\Promise\ReturnArgumentPromise * * @return $this */ public function willReturnArgument($index = 0) { if ($this->voidReturnType) { throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this); } return $this->will(new Promise\ReturnArgumentPromise($index)); } /** * Sets throw promise to the prophecy. * * @see \Prophecy\Promise\ThrowPromise * * @param string|\Exception $exception Exception class or instance * * @return $this */ public function willThrow($exception) { return $this->will(new Promise\ThrowPromise($exception)); } /** * Sets custom prediction to the prophecy. * * @param callable|Prediction\PredictionInterface $prediction * * @return $this * * @throws \Prophecy\Exception\InvalidArgumentException */ public function should($prediction) { if (is_callable($prediction)) { $prediction = new Prediction\CallbackPrediction($prediction); } if (!$prediction instanceof Prediction\PredictionInterface) { throw new InvalidArgumentException(sprintf( 'Expected callable or instance of PredictionInterface, but got %s.', gettype($prediction) )); } $this->bindToObjectProphecy(); $this->prediction = $prediction; return $this; } /** * Sets call prediction to the prophecy. * * @see \Prophecy\Prediction\CallPrediction * * @return $this */ public function shouldBeCalled() { return $this->should(new Prediction\CallPrediction); } /** * Sets no calls prediction to the prophecy. * * @see \Prophecy\Prediction\NoCallsPrediction * * @return $this */ public function shouldNotBeCalled() { return $this->should(new Prediction\NoCallsPrediction); } /** * Sets call times prediction to the prophecy. * * @see \Prophecy\Prediction\CallTimesPrediction * * @param $count * * @return $this */ public function shouldBeCalledTimes($count) { return $this->should(new Prediction\CallTimesPrediction($count)); } /** * Sets call times prediction to the prophecy. * * @see \Prophecy\Prediction\CallTimesPrediction * * @return $this */ public function shouldBeCalledOnce() { return $this->shouldBeCalledTimes(1); } /** * Checks provided prediction immediately. * * @param callable|Prediction\PredictionInterface $prediction * * @return $this * * @throws \Prophecy\Exception\InvalidArgumentException */ public function shouldHave($prediction) { if (is_callable($prediction)) { $prediction = new Prediction\CallbackPrediction($prediction); } if (!$prediction instanceof Prediction\PredictionInterface) { throw new InvalidArgumentException(sprintf( 'Expected callable or instance of PredictionInterface, but got %s.', gettype($prediction) )); } if (null === $this->promise && !$this->voidReturnType) { $this->willReturn(); } $calls = $this->getObjectProphecy()->findProphecyMethodCalls( $this->getMethodName(), $this->getArgumentsWildcard() ); try { $prediction->check($calls, $this->getObjectProphecy(), $this); $this->checkedPredictions[] = $prediction; } catch (\Exception $e) { $this->checkedPredictions[] = $prediction; throw $e; } return $this; } /** * Checks call prediction. * * @see \Prophecy\Prediction\CallPrediction * * @return $this */ public function shouldHaveBeenCalled() { return $this->shouldHave(new Prediction\CallPrediction); } /** * Checks no calls prediction. * * @see \Prophecy\Prediction\NoCallsPrediction * * @return $this */ public function shouldNotHaveBeenCalled() { return $this->shouldHave(new Prediction\NoCallsPrediction); } /** * Checks no calls prediction. * * @see \Prophecy\Prediction\NoCallsPrediction * @deprecated * * @return $this */ public function shouldNotBeenCalled() { return $this->shouldNotHaveBeenCalled(); } /** * Checks call times prediction. * * @see \Prophecy\Prediction\CallTimesPrediction * * @param int $count * * @return $this */ public function shouldHaveBeenCalledTimes($count) { return $this->shouldHave(new Prediction\CallTimesPrediction($count)); } /** * Checks call times prediction. * * @see \Prophecy\Prediction\CallTimesPrediction * * @return $this */ public function shouldHaveBeenCalledOnce() { return $this->shouldHaveBeenCalledTimes(1); } /** * Checks currently registered [with should(...)] prediction. */ public function checkPrediction() { if (null === $this->prediction) { return; } $this->shouldHave($this->prediction); } /** * Returns currently registered promise. * * @return null|Promise\PromiseInterface */ public function getPromise() { return $this->promise; } /** * Returns currently registered prediction. * * @return null|Prediction\PredictionInterface */ public function getPrediction() { return $this->prediction; } /** * Returns predictions that were checked on this object. * * @return Prediction\PredictionInterface[] */ public function getCheckedPredictions() { return $this->checkedPredictions; } /** * Returns object prophecy this method prophecy is tied to. * * @return ObjectProphecy */ public function getObjectProphecy() { return $this->objectProphecy; } /** * Returns method name. * * @return string */ public function getMethodName() { return $this->methodName; } /** * Returns arguments wildcard. * * @return Argument\ArgumentsWildcard */ public function getArgumentsWildcard() { return $this->argumentsWildcard; } /** * @return bool */ public function hasReturnVoid() { return $this->voidReturnType; } private function bindToObjectProphecy() { if ($this->bound) { return; } $this->getObjectProphecy()->addMethodProphecy($this); $this->bound = true; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prophecy; use SebastianBergmann\Comparator\ComparisonFailure; use Prophecy\Comparator\Factory as ComparatorFactory; use Prophecy\Call\Call; use Prophecy\Doubler\LazyDouble; use Prophecy\Argument\ArgumentsWildcard; use Prophecy\Call\CallCenter; use Prophecy\Exception\Prophecy\ObjectProphecyException; use Prophecy\Exception\Prophecy\MethodProphecyException; use Prophecy\Exception\Prediction\AggregateException; use Prophecy\Exception\Prediction\PredictionException; /** * Object prophecy. * * @author Konstantin Kudryashov */ class ObjectProphecy implements ProphecyInterface { private $lazyDouble; private $callCenter; private $revealer; private $comparatorFactory; /** * @var MethodProphecy[][] */ private $methodProphecies = array(); /** * Initializes object prophecy. * * @param LazyDouble $lazyDouble * @param CallCenter $callCenter * @param RevealerInterface $revealer * @param ComparatorFactory $comparatorFactory */ public function __construct( LazyDouble $lazyDouble, CallCenter $callCenter = null, RevealerInterface $revealer = null, ComparatorFactory $comparatorFactory = null ) { $this->lazyDouble = $lazyDouble; $this->callCenter = $callCenter ?: new CallCenter; $this->revealer = $revealer ?: new Revealer; $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } /** * Forces double to extend specific class. * * @param string $class * * @return $this */ public function willExtend($class) { $this->lazyDouble->setParentClass($class); return $this; } /** * Forces double to implement specific interface. * * @param string $interface * * @return $this */ public function willImplement($interface) { $this->lazyDouble->addInterface($interface); return $this; } /** * Sets constructor arguments. * * @param array $arguments * * @return $this */ public function willBeConstructedWith(array $arguments = null) { $this->lazyDouble->setArguments($arguments); return $this; } /** * Reveals double. * * @return object * * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface */ public function reveal() { $double = $this->lazyDouble->getInstance(); if (null === $double || !$double instanceof ProphecySubjectInterface) { throw new ObjectProphecyException( "Generated double must implement ProphecySubjectInterface, but it does not.\n". 'It seems you have wrongly configured doubler without required ClassPatch.', $this ); } $double->setProphecy($this); return $double; } /** * Adds method prophecy to object prophecy. * * @param MethodProphecy $methodProphecy * * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't * have arguments wildcard */ public function addMethodProphecy(MethodProphecy $methodProphecy) { $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); if (null === $argumentsWildcard) { throw new MethodProphecyException(sprintf( "Can not add prophecy for a method `%s::%s()`\n". "as you did not specify arguments wildcard for it.", get_class($this->reveal()), $methodProphecy->getMethodName() ), $methodProphecy); } $methodName = $methodProphecy->getMethodName(); if (!isset($this->methodProphecies[$methodName])) { $this->methodProphecies[$methodName] = array(); } $this->methodProphecies[$methodName][] = $methodProphecy; } /** * Returns either all or related to single method prophecies. * * @param null|string $methodName * * @return MethodProphecy[] */ public function getMethodProphecies($methodName = null) { if (null === $methodName) { return $this->methodProphecies; } if (!isset($this->methodProphecies[$methodName])) { return array(); } return $this->methodProphecies[$methodName]; } /** * Makes specific method call. * * @param string $methodName * @param array $arguments * * @return mixed */ public function makeProphecyMethodCall($methodName, array $arguments) { $arguments = $this->revealer->reveal($arguments); $return = $this->callCenter->makeCall($this, $methodName, $arguments); return $this->revealer->reveal($return); } /** * Finds calls by method name & arguments wildcard. * * @param string $methodName * @param ArgumentsWildcard $wildcard * * @return Call[] */ public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) { return $this->callCenter->findCalls($methodName, $wildcard); } /** * Checks that registered method predictions do not fail. * * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail */ public function checkProphecyMethodsPredictions() { $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); $exception->setObjectProphecy($this); foreach ($this->methodProphecies as $prophecies) { foreach ($prophecies as $prophecy) { try { $prophecy->checkPrediction(); } catch (PredictionException $e) { $exception->append($e); } } } if (count($exception->getExceptions())) { throw $exception; } } /** * Creates new method prophecy using specified method name and arguments. * * @param string $methodName * @param array $arguments * * @return MethodProphecy */ public function __call($methodName, array $arguments) { $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); foreach ($this->getMethodProphecies($methodName) as $prophecy) { $argumentsWildcard = $prophecy->getArgumentsWildcard(); $comparator = $this->comparatorFactory->getComparatorFor( $argumentsWildcard, $arguments ); try { $comparator->assertEquals($argumentsWildcard, $arguments); return $prophecy; } catch (ComparisonFailure $failure) {} } return new MethodProphecy($this, $methodName, $arguments); } /** * Tries to get property value from double. * * @param string $name * * @return mixed */ public function __get($name) { return $this->reveal()->$name; } /** * Tries to set property value to double. * * @param string $name * @param mixed $value */ public function __set($name, $value) { $this->reveal()->$name = $this->revealer->reveal($value); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prophecy; /** * Prophecies revealer interface. * * @author Konstantin Kudryashov */ interface RevealerInterface { /** * Unwraps value(s). * * @param mixed $value * * @return mixed */ public function reveal($value); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prophecy; /** * Core Prophecy interface. * * @author Konstantin Kudryashov */ interface ProphecyInterface { /** * Reveals prophecy object (double) . * * @return object */ public function reveal(); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prophecy; /** * Controllable doubles interface. * * @author Konstantin Kudryashov */ interface ProphecySubjectInterface { /** * Sets subject prophecy. * * @param ProphecyInterface $prophecy */ public function setProphecy(ProphecyInterface $prophecy); /** * Returns subject prophecy. * * @return ProphecyInterface */ public function getProphecy(); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prophecy; /** * Basic prophecies revealer. * * @author Konstantin Kudryashov */ class Revealer implements RevealerInterface { /** * Unwraps value(s). * * @param mixed $value * * @return mixed */ public function reveal($value) { if (is_array($value)) { return array_map(array($this, __FUNCTION__), $value); } if (!is_object($value)) { return $value; } if ($value instanceof ProphecyInterface) { $value = $value->reveal(); } return $value; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler; use Doctrine\Instantiator\Instantiator; use Prophecy\Doubler\ClassPatch\ClassPatchInterface; use Prophecy\Doubler\Generator\ClassMirror; use Prophecy\Doubler\Generator\ClassCreator; use Prophecy\Exception\InvalidArgumentException; use ReflectionClass; /** * Cached class doubler. * Prevents mirroring/creation of the same structure twice. * * @author Konstantin Kudryashov */ class Doubler { private $mirror; private $creator; private $namer; /** * @var ClassPatchInterface[] */ private $patches = array(); /** * @var \Doctrine\Instantiator\Instantiator */ private $instantiator; /** * Initializes doubler. * * @param ClassMirror $mirror * @param ClassCreator $creator * @param NameGenerator $namer */ public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, NameGenerator $namer = null) { $this->mirror = $mirror ?: new ClassMirror; $this->creator = $creator ?: new ClassCreator; $this->namer = $namer ?: new NameGenerator; } /** * Returns list of registered class patches. * * @return ClassPatchInterface[] */ public function getClassPatches() { return $this->patches; } /** * Registers new class patch. * * @param ClassPatchInterface $patch */ public function registerClassPatch(ClassPatchInterface $patch) { $this->patches[] = $patch; @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { return $patch2->getPriority() - $patch1->getPriority(); }); } /** * Creates double from specific class or/and list of interfaces. * * @param ReflectionClass $class * @param ReflectionClass[] $interfaces Array of ReflectionClass instances * @param array $args Constructor arguments * * @return DoubleInterface * * @throws \Prophecy\Exception\InvalidArgumentException */ public function double(ReflectionClass $class = null, array $interfaces, array $args = null) { foreach ($interfaces as $interface) { if (!$interface instanceof ReflectionClass) { throw new InvalidArgumentException(sprintf( "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". "a second argument to `Doubler::double(...)`, but got %s.", is_object($interface) ? get_class($interface).' class' : gettype($interface) )); } } $classname = $this->createDoubleClass($class, $interfaces); $reflection = new ReflectionClass($classname); if (null !== $args) { return $reflection->newInstanceArgs($args); } if ((null === $constructor = $reflection->getConstructor()) || ($constructor->isPublic() && !$constructor->isFinal())) { return $reflection->newInstance(); } if (!$this->instantiator) { $this->instantiator = new Instantiator(); } return $this->instantiator->instantiate($classname); } /** * Creates double class and returns its FQN. * * @param ReflectionClass $class * @param ReflectionClass[] $interfaces * * @return string */ protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) { $name = $this->namer->name($class, $interfaces); $node = $this->mirror->reflect($class, $interfaces); foreach ($this->patches as $patch) { if ($patch->supports($node)) { $patch->apply($node); } } $this->creator->create($name, $node); return $name; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler; /** * Core double interface. * All doubled classes will implement this one. * * @author Konstantin Kudryashov */ interface DoubleInterface { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler; use ReflectionClass; /** * Name generator. * Generates classname for double. * * @author Konstantin Kudryashov */ class NameGenerator { private static $counter = 1; /** * Generates name. * * @param ReflectionClass $class * @param ReflectionClass[] $interfaces * * @return string */ public function name(ReflectionClass $class = null, array $interfaces) { $parts = array(); if (null !== $class) { $parts[] = $class->getName(); } else { foreach ($interfaces as $interface) { $parts[] = $interface->getShortName(); } } if (!count($parts)) { $parts[] = 'stdClass'; } return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler; use Prophecy\Exception\Doubler\DoubleException; use Prophecy\Exception\Doubler\ClassNotFoundException; use Prophecy\Exception\Doubler\InterfaceNotFoundException; use ReflectionClass; /** * Lazy double. * Gives simple interface to describe double before creating it. * * @author Konstantin Kudryashov */ class LazyDouble { private $doubler; private $class; private $interfaces = array(); private $arguments = null; private $double; /** * Initializes lazy double. * * @param Doubler $doubler */ public function __construct(Doubler $doubler) { $this->doubler = $doubler; } /** * Tells doubler to use specific class as parent one for double. * * @param string|ReflectionClass $class * * @throws \Prophecy\Exception\Doubler\ClassNotFoundException * @throws \Prophecy\Exception\Doubler\DoubleException */ public function setParentClass($class) { if (null !== $this->double) { throw new DoubleException('Can not extend class with already instantiated double.'); } if (!$class instanceof ReflectionClass) { if (!class_exists($class)) { throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class); } $class = new ReflectionClass($class); } $this->class = $class; } /** * Tells doubler to implement specific interface with double. * * @param string|ReflectionClass $interface * * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException * @throws \Prophecy\Exception\Doubler\DoubleException */ public function addInterface($interface) { if (null !== $this->double) { throw new DoubleException( 'Can not implement interface with already instantiated double.' ); } if (!$interface instanceof ReflectionClass) { if (!interface_exists($interface)) { throw new InterfaceNotFoundException( sprintf('Interface %s not found.', $interface), $interface ); } $interface = new ReflectionClass($interface); } $this->interfaces[] = $interface; } /** * Sets constructor arguments. * * @param array $arguments */ public function setArguments(array $arguments = null) { $this->arguments = $arguments; } /** * Creates double instance or returns already created one. * * @return DoubleInterface */ public function getInstance() { if (null === $this->double) { if (null !== $this->arguments) { return $this->double = $this->doubler->double( $this->class, $this->interfaces, $this->arguments ); } $this->double = $this->doubler->double($this->class, $this->interfaces); } return $this->double; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler; use ReflectionClass; /** * Cached class doubler. * Prevents mirroring/creation of the same structure twice. * * @author Konstantin Kudryashov */ class CachedDoubler extends Doubler { private $classes = array(); /** * {@inheritdoc} */ public function registerClassPatch(ClassPatch\ClassPatchInterface $patch) { $this->classes[] = array(); parent::registerClassPatch($patch); } /** * {@inheritdoc} */ protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) { $classId = $this->generateClassId($class, $interfaces); if (isset($this->classes[$classId])) { return $this->classes[$classId]; } return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces); } /** * @param ReflectionClass $class * @param ReflectionClass[] $interfaces * * @return string */ private function generateClassId(ReflectionClass $class = null, array $interfaces) { $parts = array(); if (null !== $class) { $parts[] = $class->getName(); } foreach ($interfaces as $interface) { $parts[] = $interface->getName(); } sort($parts); return md5(implode('', $parts)); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator; /** * Class code creator. * Generates PHP code for specific class node tree. * * @author Konstantin Kudryashov */ class ClassCodeGenerator { /** * @var TypeHintReference */ private $typeHintReference; public function __construct(TypeHintReference $typeHintReference = null) { $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); } /** * Generates PHP code for class node. * * @param string $classname * @param Node\ClassNode $class * * @return string */ public function generate($classname, Node\ClassNode $class) { $parts = explode('\\', $classname); $classname = array_pop($parts); $namespace = implode('\\', $parts); $code = sprintf("class %s extends \%s implements %s {\n", $classname, $class->getParentClass(), implode(', ', array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) ) ); foreach ($class->getProperties() as $name => $visibility) { $code .= sprintf("%s \$%s;\n", $visibility, $name); } $code .= "\n"; foreach ($class->getMethods() as $method) { $code .= $this->generateMethod($method)."\n"; } $code .= "\n}"; return sprintf("namespace %s {\n%s\n}", $namespace, $code); } private function generateMethod(Node\MethodNode $method) { $php = sprintf("%s %s function %s%s(%s)%s {\n", $method->getVisibility(), $method->isStatic() ? 'static' : '', $method->returnsReference() ? '&':'', $method->getName(), implode(', ', $this->generateArguments($method->getArguments())), $this->getReturnType($method) ); $php .= $method->getCode()."\n"; return $php.'}'; } /** * @return string */ private function getReturnType(Node\MethodNode $method) { if (version_compare(PHP_VERSION, '7.1', '>=')) { if ($method->hasReturnType()) { return $method->hasNullableReturnType() ? sprintf(': ?%s', $method->getReturnType()) : sprintf(': %s', $method->getReturnType()); } } if (version_compare(PHP_VERSION, '7.0', '>=')) { return $method->hasReturnType() && $method->getReturnType() !== 'void' ? sprintf(': %s', $method->getReturnType()) : ''; } return ''; } private function generateArguments(array $arguments) { $typeHintReference = $this->typeHintReference; return array_map(function (Node\ArgumentNode $argument) use ($typeHintReference) { $php = ''; if (version_compare(PHP_VERSION, '7.1', '>=')) { $php .= $argument->isNullable() ? '?' : ''; } if ($hint = $argument->getTypeHint()) { $php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint; } $php .= ' '.($argument->isPassedByReference() ? '&' : ''); $php .= $argument->isVariadic() ? '...' : ''; $php .= '$'.$argument->getName(); if ($argument->isOptional() && !$argument->isVariadic()) { $php .= ' = '.var_export($argument->getDefault(), true); } return $php; }, $arguments); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator; use Prophecy\Exception\InvalidArgumentException; use Prophecy\Exception\Doubler\ClassMirrorException; use ReflectionClass; use ReflectionMethod; use ReflectionParameter; /** * Class mirror. * Core doubler class. Mirrors specific class and/or interfaces into class node tree. * * @author Konstantin Kudryashov */ class ClassMirror { private static $reflectableMethods = array( '__construct', '__destruct', '__sleep', '__wakeup', '__toString', '__call', '__invoke' ); /** * Reflects provided arguments into class node. * * @param ReflectionClass $class * @param ReflectionClass[] $interfaces * * @return Node\ClassNode * * @throws \Prophecy\Exception\InvalidArgumentException */ public function reflect(ReflectionClass $class = null, array $interfaces) { $node = new Node\ClassNode; if (null !== $class) { if (true === $class->isInterface()) { throw new InvalidArgumentException(sprintf( "Could not reflect %s as a class, because it\n". "is interface - use the second argument instead.", $class->getName() )); } $this->reflectClassToNode($class, $node); } foreach ($interfaces as $interface) { if (!$interface instanceof ReflectionClass) { throw new InvalidArgumentException(sprintf( "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". "a second argument to `ClassMirror::reflect(...)`, but got %s.", is_object($interface) ? get_class($interface).' class' : gettype($interface) )); } if (false === $interface->isInterface()) { throw new InvalidArgumentException(sprintf( "Could not reflect %s as an interface, because it\n". "is class - use the first argument instead.", $interface->getName() )); } $this->reflectInterfaceToNode($interface, $node); } $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface'); return $node; } private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node) { if (true === $class->isFinal()) { throw new ClassMirrorException(sprintf( 'Could not reflect class %s as it is marked final.', $class->getName() ), $class); } $node->setParentClass($class->getName()); foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { if (false === $method->isProtected()) { continue; } $this->reflectMethodToNode($method, $node); } foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { if (0 === strpos($method->getName(), '_') && !in_array($method->getName(), self::$reflectableMethods)) { continue; } if (true === $method->isFinal()) { $node->addUnextendableMethod($method->getName()); continue; } $this->reflectMethodToNode($method, $node); } } private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node) { $node->addInterface($interface->getName()); foreach ($interface->getMethods() as $method) { $this->reflectMethodToNode($method, $node); } } private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode) { $node = new Node\MethodNode($method->getName()); if (true === $method->isProtected()) { $node->setVisibility('protected'); } if (true === $method->isStatic()) { $node->setStatic(); } if (true === $method->returnsReference()) { $node->setReturnsReference(); } if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) { $returnType = (string) $method->getReturnType(); $returnTypeLower = strtolower($returnType); if ('self' === $returnTypeLower) { $returnType = $method->getDeclaringClass()->getName(); } if ('parent' === $returnTypeLower) { $returnType = $method->getDeclaringClass()->getParentClass()->getName(); } $node->setReturnType($returnType); if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) { $node->setNullableReturnType(true); } } if (is_array($params = $method->getParameters()) && count($params)) { foreach ($params as $param) { $this->reflectArgumentToNode($param, $node); } } $classNode->addMethod($node); } private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode) { $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); $node = new Node\ArgumentNode($name); $node->setTypeHint($this->getTypeHint($parameter)); if ($this->isVariadic($parameter)) { $node->setAsVariadic(); } if ($this->hasDefaultValue($parameter)) { $node->setDefault($this->getDefaultValue($parameter)); } if ($parameter->isPassedByReference()) { $node->setAsPassedByReference(); } $node->setAsNullable($this->isNullable($parameter)); $methodNode->addArgument($node); } private function hasDefaultValue(ReflectionParameter $parameter) { if ($this->isVariadic($parameter)) { return false; } if ($parameter->isDefaultValueAvailable()) { return true; } return $parameter->isOptional() || $this->isNullable($parameter); } private function getDefaultValue(ReflectionParameter $parameter) { if (!$parameter->isDefaultValueAvailable()) { return null; } return $parameter->getDefaultValue(); } private function getTypeHint(ReflectionParameter $parameter) { if (null !== $className = $this->getParameterClassName($parameter)) { return $className; } if (true === $parameter->isArray()) { return 'array'; } if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) { return 'callable'; } if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) { return (string) $parameter->getType(); } return null; } private function isVariadic(ReflectionParameter $parameter) { return PHP_VERSION_ID >= 50600 && $parameter->isVariadic(); } private function isNullable(ReflectionParameter $parameter) { return $parameter->allowsNull() && null !== $this->getTypeHint($parameter); } private function getParameterClassName(ReflectionParameter $parameter) { try { return $parameter->getClass() ? $parameter->getClass()->getName() : null; } catch (\ReflectionException $e) { preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches); return isset($matches[1]) ? $matches[1] : null; } } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator\Node; use Prophecy\Doubler\Generator\TypeHintReference; use Prophecy\Exception\InvalidArgumentException; /** * Method node. * * @author Konstantin Kudryashov */ class MethodNode { private $name; private $code; private $visibility = 'public'; private $static = false; private $returnsReference = false; private $returnType; private $nullableReturnType = false; /** * @var ArgumentNode[] */ private $arguments = array(); /** * @var TypeHintReference */ private $typeHintReference; /** * @param string $name * @param string $code */ public function __construct($name, $code = null, TypeHintReference $typeHintReference = null) { $this->name = $name; $this->code = $code; $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); } public function getVisibility() { return $this->visibility; } /** * @param string $visibility */ public function setVisibility($visibility) { $visibility = strtolower($visibility); if (!in_array($visibility, array('public', 'private', 'protected'))) { throw new InvalidArgumentException(sprintf( '`%s` method visibility is not supported.', $visibility )); } $this->visibility = $visibility; } public function isStatic() { return $this->static; } public function setStatic($static = true) { $this->static = (bool) $static; } public function returnsReference() { return $this->returnsReference; } public function setReturnsReference() { $this->returnsReference = true; } public function getName() { return $this->name; } public function addArgument(ArgumentNode $argument) { $this->arguments[] = $argument; } /** * @return ArgumentNode[] */ public function getArguments() { return $this->arguments; } public function hasReturnType() { return null !== $this->returnType; } /** * @param string $type */ public function setReturnType($type = null) { if ($type === '' || $type === null) { $this->returnType = null; return; } $typeMap = array( 'double' => 'float', 'real' => 'float', 'boolean' => 'bool', 'integer' => 'int', ); if (isset($typeMap[$type])) { $type = $typeMap[$type]; } $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? $type : '\\' . ltrim($type, '\\'); } public function getReturnType() { return $this->returnType; } /** * @param bool $bool */ public function setNullableReturnType($bool = true) { $this->nullableReturnType = (bool) $bool; } /** * @return bool */ public function hasNullableReturnType() { return $this->nullableReturnType; } /** * @param string $code */ public function setCode($code) { $this->code = $code; } public function getCode() { if ($this->returnsReference) { return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; } return (string) $this->code; } public function useParentCode() { $this->code = sprintf( 'return parent::%s(%s);', $this->getName(), implode(', ', array_map(array($this, 'generateArgument'), $this->arguments) ) ); } private function generateArgument(ArgumentNode $arg) { $argument = '$'.$arg->getName(); if ($arg->isVariadic()) { $argument = '...'.$argument; } return $argument; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator\Node; /** * Argument node. * * @author Konstantin Kudryashov */ class ArgumentNode { private $name; private $typeHint; private $default; private $optional = false; private $byReference = false; private $isVariadic = false; private $isNullable = false; /** * @param string $name */ public function __construct($name) { $this->name = $name; } public function getName() { return $this->name; } public function getTypeHint() { return $this->typeHint; } public function setTypeHint($typeHint = null) { $this->typeHint = $typeHint; } public function hasDefault() { return $this->isOptional() && !$this->isVariadic(); } public function getDefault() { return $this->default; } public function setDefault($default = null) { $this->optional = true; $this->default = $default; } public function isOptional() { return $this->optional; } public function setAsPassedByReference($byReference = true) { $this->byReference = $byReference; } public function isPassedByReference() { return $this->byReference; } public function setAsVariadic($isVariadic = true) { $this->isVariadic = $isVariadic; } public function isVariadic() { return $this->isVariadic; } public function isNullable() { return $this->isNullable; } public function setAsNullable($isNullable = true) { $this->isNullable = $isNullable; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator\Node; use Prophecy\Exception\Doubler\MethodNotExtendableException; use Prophecy\Exception\InvalidArgumentException; /** * Class node. * * @author Konstantin Kudryashov */ class ClassNode { private $parentClass = 'stdClass'; private $interfaces = array(); private $properties = array(); private $unextendableMethods = array(); /** * @var MethodNode[] */ private $methods = array(); public function getParentClass() { return $this->parentClass; } /** * @param string $class */ public function setParentClass($class) { $this->parentClass = $class ?: 'stdClass'; } /** * @return string[] */ public function getInterfaces() { return $this->interfaces; } /** * @param string $interface */ public function addInterface($interface) { if ($this->hasInterface($interface)) { return; } array_unshift($this->interfaces, $interface); } /** * @param string $interface * * @return bool */ public function hasInterface($interface) { return in_array($interface, $this->interfaces); } public function getProperties() { return $this->properties; } public function addProperty($name, $visibility = 'public') { $visibility = strtolower($visibility); if (!in_array($visibility, array('public', 'private', 'protected'))) { throw new InvalidArgumentException(sprintf( '`%s` property visibility is not supported.', $visibility )); } $this->properties[$name] = $visibility; } /** * @return MethodNode[] */ public function getMethods() { return $this->methods; } public function addMethod(MethodNode $method, $force = false) { if (!$this->isExtendable($method->getName())){ $message = sprintf( 'Method `%s` is not extendable, so can not be added.', $method->getName() ); throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); } if ($force || !isset($this->methods[$method->getName()])) { $this->methods[$method->getName()] = $method; } } public function removeMethod($name) { unset($this->methods[$name]); } /** * @param string $name * * @return MethodNode|null */ public function getMethod($name) { return $this->hasMethod($name) ? $this->methods[$name] : null; } /** * @param string $name * * @return bool */ public function hasMethod($name) { return isset($this->methods[$name]); } /** * @return string[] */ public function getUnextendableMethods() { return $this->unextendableMethods; } /** * @param string $unextendableMethod */ public function addUnextendableMethod($unextendableMethod) { if (!$this->isExtendable($unextendableMethod)){ return; } $this->unextendableMethods[] = $unextendableMethod; } /** * @param string $method * @return bool */ public function isExtendable($method) { return !in_array($method, $this->unextendableMethods); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator; use Prophecy\Exception\Doubler\ClassCreatorException; /** * Class creator. * Creates specific class in current environment. * * @author Konstantin Kudryashov */ class ClassCreator { private $generator; /** * Initializes creator. * * @param ClassCodeGenerator $generator */ public function __construct(ClassCodeGenerator $generator = null) { $this->generator = $generator ?: new ClassCodeGenerator; } /** * Creates class. * * @param string $classname * @param Node\ClassNode $class * * @return mixed * * @throws \Prophecy\Exception\Doubler\ClassCreatorException */ public function create($classname, Node\ClassNode $class) { $code = $this->generator->generate($classname, $class); $return = eval($code); if (!class_exists($classname, false)) { if (count($class->getInterfaces())) { throw new ClassCreatorException(sprintf( 'Could not double `%s` and implement interfaces: [%s].', $class->getParentClass(), implode(', ', $class->getInterfaces()) ), $class); } throw new ClassCreatorException( sprintf('Could not double `%s`.', $class->getParentClass()), $class ); } return $return; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\Generator; /** * Reflection interface. * All reflected classes implement this interface. * * @author Konstantin Kudryashov */ interface ReflectionInterface { } = 50400; case 'bool': case 'float': case 'int': case 'string': return PHP_VERSION_ID >= 70000; case 'iterable': return PHP_VERSION_ID >= 70100; case 'object': return PHP_VERSION_ID >= 70200; default: return false; } } public function isBuiltInReturnTypeHint($type) { if ($type === 'void') { return PHP_VERSION_ID >= 70100; } return $this->isBuiltInParamTypeHint($type); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; use Prophecy\Doubler\Generator\Node\MethodNode; /** * SplFileInfo patch. * Makes SplFileInfo and derivative classes usable with Prophecy. * * @author Konstantin Kudryashov */ class SplFileInfoPatch implements ClassPatchInterface { /** * Supports everything that extends SplFileInfo. * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node) { if (null === $node->getParentClass()) { return false; } return 'SplFileInfo' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'SplFileInfo') ; } /** * Updated constructor code to call parent one with dummy file argument. * * @param ClassNode $node */ public function apply(ClassNode $node) { if ($node->hasMethod('__construct')) { $constructor = $node->getMethod('__construct'); } else { $constructor = new MethodNode('__construct'); $node->addMethod($constructor); } if ($this->nodeIsDirectoryIterator($node)) { $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); return; } if ($this->nodeIsSplFileObject($node)) { $filePath = str_replace('\\','\\\\',__FILE__); $constructor->setCode('return parent::__construct("' . $filePath .'");'); return; } if ($this->nodeIsSymfonySplFileInfo($node)) { $filePath = str_replace('\\','\\\\',__FILE__); $constructor->setCode('return parent::__construct("' . $filePath .'", "", "");'); return; } $constructor->useParentCode(); } /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher - earlier) */ public function getPriority() { return 50; } /** * @param ClassNode $node * @return boolean */ private function nodeIsDirectoryIterator(ClassNode $node) { $parent = $node->getParentClass(); return 'DirectoryIterator' === $parent || is_subclass_of($parent, 'DirectoryIterator'); } /** * @param ClassNode $node * @return boolean */ private function nodeIsSplFileObject(ClassNode $node) { $parent = $node->getParentClass(); return 'SplFileObject' === $parent || is_subclass_of($parent, 'SplFileObject'); } /** * @param ClassNode $node * @return boolean */ private function nodeIsSymfonySplFileInfo(ClassNode $node) { $parent = $node->getParentClass(); return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; /** * Class patch interface. * Class patches extend doubles functionality or help * Prophecy to avoid some internal PHP bugs. * * @author Konstantin Kudryashov */ interface ClassPatchInterface { /** * Checks if patch supports specific class node. * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node); /** * Applies patch to the specific class node. * * @param ClassNode $node * @return void */ public function apply(ClassNode $node); /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher - earlier) */ public function getPriority(); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; use Prophecy\Doubler\Generator\Node\MethodNode; /** * Traversable interface patch. * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. * * @author Konstantin Kudryashov */ class TraversablePatch implements ClassPatchInterface { /** * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node) { if (in_array('Iterator', $node->getInterfaces())) { return false; } if (in_array('IteratorAggregate', $node->getInterfaces())) { return false; } foreach ($node->getInterfaces() as $interface) { if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) { continue; } if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) { continue; } if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) { continue; } return true; } return false; } /** * Forces class to implement Iterator interface. * * @param ClassNode $node */ public function apply(ClassNode $node) { $node->addInterface('Iterator'); $node->addMethod(new MethodNode('current')); $node->addMethod(new MethodNode('key')); $node->addMethod(new MethodNode('next')); $node->addMethod(new MethodNode('rewind')); $node->addMethod(new MethodNode('valid')); } /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher - earlier) */ public function getPriority() { return 100; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; use Prophecy\Doubler\Generator\Node\MethodNode; /** * Disable constructor. * Makes all constructor arguments optional. * * @author Konstantin Kudryashov */ class DisableConstructorPatch implements ClassPatchInterface { /** * Checks if class has `__construct` method. * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node) { return true; } /** * Makes all class constructor arguments optional. * * @param ClassNode $node */ public function apply(ClassNode $node) { if (!$node->hasMethod('__construct')) { $node->addMethod(new MethodNode('__construct', '')); return; } $constructor = $node->getMethod('__construct'); foreach ($constructor->getArguments() as $argument) { $argument->setDefault(null); } $constructor->setCode(<< * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; /** * ReflectionClass::newInstance patch. * Makes first argument of newInstance optional, since it works but signature is misleading * * @author Florian Klein */ class ReflectionClassNewInstancePatch implements ClassPatchInterface { /** * Supports ReflectionClass * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node) { return 'ReflectionClass' === $node->getParentClass(); } /** * Updates newInstance's first argument to make it optional * * @param ClassNode $node */ public function apply(ClassNode $node) { foreach ($node->getMethod('newInstance')->getArguments() as $argument) { $argument->setDefault(null); } } /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher = earlier) */ public function getPriority() { return 50; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; /** * Exception patch for HHVM to remove the stubs from special methods * * @author Christophe Coevoet */ class HhvmExceptionPatch implements ClassPatchInterface { /** * Supports exceptions on HHVM. * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node) { if (!defined('HHVM_VERSION')) { return false; } return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception'); } /** * Removes special exception static methods from the doubled methods. * * @param ClassNode $node * * @return void */ public function apply(ClassNode $node) { if ($node->hasMethod('setTraceOptions')) { $node->getMethod('setTraceOptions')->useParentCode(); } if ($node->hasMethod('getTraceOptions')) { $node->getMethod('getTraceOptions')->useParentCode(); } } /** * {@inheritdoc} */ public function getPriority() { return -50; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; /** * Remove method functionality from the double which will clash with php keywords. * * @author Milan Magudia */ class KeywordPatch implements ClassPatchInterface { /** * Support any class * * @param ClassNode $node * * @return boolean */ public function supports(ClassNode $node) { return true; } /** * Remove methods that clash with php keywords * * @param ClassNode $node */ public function apply(ClassNode $node) { $methodNames = array_keys($node->getMethods()); $methodsToRemove = array_intersect($methodNames, $this->getKeywords()); foreach ($methodsToRemove as $methodName) { $node->removeMethod($methodName); } } /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher - earlier) */ public function getPriority() { return 49; } /** * Returns array of php keywords. * * @return array */ private function getKeywords() { if (\PHP_VERSION_ID >= 70000) { return array('__halt_compiler'); } return array( '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'finally', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'yield', ); } } implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); } /** * @param ClassNode $node * @return bool */ private function implementsAThrowableInterface(ClassNode $node) { foreach ($node->getInterfaces() as $type) { if (is_a($type, 'Throwable', true)) { return true; } } return false; } /** * @param ClassNode $node * @return bool */ private function doesNotExtendAThrowableClass(ClassNode $node) { return !is_a($node->getParentClass(), 'Throwable', true); } /** * Applies patch to the specific class node. * * @param ClassNode $node * * @return void */ public function apply(ClassNode $node) { $this->checkItCanBeDoubled($node); $this->setParentClassToException($node); } private function checkItCanBeDoubled(ClassNode $node) { $className = $node->getParentClass(); if ($className !== 'stdClass') { throw new ClassCreatorException( sprintf( 'Cannot double concrete class %s as well as implement Traversable', $className ), $node ); } } private function setParentClassToException(ClassNode $node) { $node->setParentClass('Exception'); $node->removeMethod('getMessage'); $node->removeMethod('getCode'); $node->removeMethod('getFile'); $node->removeMethod('getLine'); $node->removeMethod('getTrace'); $node->removeMethod('getPrevious'); $node->removeMethod('getNext'); $node->removeMethod('getTraceAsString'); } /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher - earlier) */ public function getPriority() { return 100; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; use Prophecy\Doubler\Generator\Node\MethodNode; use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; /** * Discover Magical API using "@method" PHPDoc format. * * @author Thomas Tourlourat * @author Kévin Dunglas * @author Théo FIDRY */ class MagicCallPatch implements ClassPatchInterface { private $tagRetriever; public function __construct(MethodTagRetrieverInterface $tagRetriever = null) { $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; } /** * Support any class * * @param ClassNode $node * * @return boolean */ public function supports(ClassNode $node) { return true; } /** * Discover Magical API * * @param ClassNode $node */ public function apply(ClassNode $node) { $types = array_filter($node->getInterfaces(), function ($interface) { return 0 !== strpos($interface, 'Prophecy\\'); }); $types[] = $node->getParentClass(); foreach ($types as $type) { $reflectionClass = new \ReflectionClass($type); while ($reflectionClass) { $tagList = $this->tagRetriever->getTagList($reflectionClass); foreach ($tagList as $tag) { $methodName = $tag->getMethodName(); if (empty($methodName)) { continue; } if (!$reflectionClass->hasMethod($methodName)) { $methodNode = new MethodNode($methodName); $methodNode->setStatic($tag->isStatic()); $node->addMethod($methodNode); } } $reflectionClass = $reflectionClass->getParentClass(); } } } /** * Returns patch priority, which determines when patch will be applied. * * @return integer Priority number (higher - earlier) */ public function getPriority() { return 50; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Doubler\ClassPatch; use Prophecy\Doubler\Generator\Node\ClassNode; use Prophecy\Doubler\Generator\Node\MethodNode; use Prophecy\Doubler\Generator\Node\ArgumentNode; /** * Add Prophecy functionality to the double. * This is a core class patch for Prophecy. * * @author Konstantin Kudryashov */ class ProphecySubjectPatch implements ClassPatchInterface { /** * Always returns true. * * @param ClassNode $node * * @return bool */ public function supports(ClassNode $node) { return true; } /** * Apply Prophecy functionality to class node. * * @param ClassNode $node */ public function apply(ClassNode $node) { $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface'); $node->addProperty('objectProphecy', 'private'); foreach ($node->getMethods() as $name => $method) { if ('__construct' === strtolower($name)) { continue; } if ($method->getReturnType() === 'void') { $method->setCode( '$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' ); } else { $method->setCode( 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' ); } } $prophecySetter = new MethodNode('setProphecy'); $prophecyArgument = new ArgumentNode('prophecy'); $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface'); $prophecySetter->addArgument($prophecyArgument); $prophecySetter->setCode('$this->objectProphecy = $prophecy;'); $prophecyGetter = new MethodNode('getProphecy'); $prophecyGetter->setCode('return $this->objectProphecy;'); if ($node->hasMethod('__call')) { $__call = $node->getMethod('__call'); } else { $__call = new MethodNode('__call'); $__call->addArgument(new ArgumentNode('name')); $__call->addArgument(new ArgumentNode('arguments')); $node->addMethod($__call, true); } $__call->setCode(<<getProphecy(), func_get_arg(0) ); PHP ); $node->addMethod($prophecySetter, true); $node->addMethod($prophecyGetter, true); } /** * Returns patch priority, which determines when patch will be applied. * * @return int Priority number (higher - earlier) */ public function getPriority() { return 0; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\PhpDocumentor; use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; use phpDocumentor\Reflection\DocBlock\Tags\Method; /** * @author Théo FIDRY * * @internal */ interface MethodTagRetrieverInterface { /** * @param \ReflectionClass $reflectionClass * * @return LegacyMethodTag[]|Method[] */ public function getTagList(\ReflectionClass $reflectionClass); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\PhpDocumentor; use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; use phpDocumentor\Reflection\DocBlock\Tags\Method; /** * @author Théo FIDRY * * @internal */ final class ClassAndInterfaceTagRetriever implements MethodTagRetrieverInterface { private $classRetriever; public function __construct(MethodTagRetrieverInterface $classRetriever = null) { if (null !== $classRetriever) { $this->classRetriever = $classRetriever; return; } $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory') ? new ClassTagRetriever() : new LegacyClassTagRetriever() ; } /** * @param \ReflectionClass $reflectionClass * * @return LegacyMethodTag[]|Method[] */ public function getTagList(\ReflectionClass $reflectionClass) { return array_merge( $this->classRetriever->getTagList($reflectionClass), $this->getInterfacesTagList($reflectionClass) ); } /** * @param \ReflectionClass $reflectionClass * * @return LegacyMethodTag[]|Method[] */ private function getInterfacesTagList(\ReflectionClass $reflectionClass) { $interfaces = $reflectionClass->getInterfaces(); $tagList = array(); foreach($interfaces as $interface) { $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface)); } return $tagList; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\PhpDocumentor; use phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; /** * @author Théo FIDRY * * @internal */ final class LegacyClassTagRetriever implements MethodTagRetrieverInterface { /** * @param \ReflectionClass $reflectionClass * * @return LegacyMethodTag[] */ public function getTagList(\ReflectionClass $reflectionClass) { $phpdoc = new DocBlock($reflectionClass->getDocComment()); return $phpdoc->getTagsByName('method'); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\PhpDocumentor; use phpDocumentor\Reflection\DocBlock\Tags\Method; use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\Types\ContextFactory; /** * @author Théo FIDRY * * @internal */ final class ClassTagRetriever implements MethodTagRetrieverInterface { private $docBlockFactory; private $contextFactory; public function __construct() { $this->docBlockFactory = DocBlockFactory::createInstance(); $this->contextFactory = new ContextFactory(); } /** * @param \ReflectionClass $reflectionClass * * @return Method[] */ public function getTagList(\ReflectionClass $reflectionClass) { try { $phpdoc = $this->docBlockFactory->create( $reflectionClass, $this->contextFactory->createFromReflector($reflectionClass) ); return $phpdoc->getTagsByName('method'); } catch (\InvalidArgumentException $e) { return array(); } } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Call; use Prophecy\Exception\Prophecy\ObjectProphecyException; use Prophecy\Prophecy\ObjectProphecy; class UnexpectedCallException extends ObjectProphecyException { private $methodName; private $arguments; public function __construct($message, ObjectProphecy $objectProphecy, $methodName, array $arguments) { parent::__construct($message, $objectProphecy); $this->methodName = $methodName; $this->arguments = $arguments; } public function getMethodName() { return $this->methodName; } public function getArguments() { return $this->arguments; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prophecy; use Prophecy\Prophecy\MethodProphecy; class MethodProphecyException extends ObjectProphecyException { private $methodProphecy; public function __construct($message, MethodProphecy $methodProphecy) { parent::__construct($message, $methodProphecy->getObjectProphecy()); $this->methodProphecy = $methodProphecy; } /** * @return MethodProphecy */ public function getMethodProphecy() { return $this->methodProphecy; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prophecy; use Prophecy\Exception\Exception; interface ProphecyException extends Exception { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prophecy; use Prophecy\Prophecy\ObjectProphecy; class ObjectProphecyException extends \RuntimeException implements ProphecyException { private $objectProphecy; public function __construct($message, ObjectProphecy $objectProphecy) { parent::__construct($message); $this->objectProphecy = $objectProphecy; } /** * @return ObjectProphecy */ public function getObjectProphecy() { return $this->objectProphecy; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; use Prophecy\Exception\Exception; interface DoublerException extends Exception { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; use RuntimeException; class DoubleException extends RuntimeException implements DoublerException { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; use Prophecy\Doubler\Generator\Node\ClassNode; class ClassCreatorException extends \RuntimeException implements DoublerException { private $node; public function __construct($message, ClassNode $node) { parent::__construct($message); $this->node = $node; } public function getClassNode() { return $this->node; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; class ClassNotFoundException extends DoubleException { private $classname; /** * @param string $message * @param string $classname */ public function __construct($message, $classname) { parent::__construct($message); $this->classname = $classname; } public function getClassname() { return $this->classname; } } methodName = $methodName; $this->className = $className; } /** * @return string */ public function getMethodName() { return $this->methodName; } /** * @return string */ public function getClassName() { return $this->className; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; class MethodNotFoundException extends DoubleException { /** * @var string|object */ private $classname; /** * @var string */ private $methodName; /** * @var array */ private $arguments; /** * @param string $message * @param string|object $classname * @param string $methodName * @param null|Argument\ArgumentsWildcard|array $arguments */ public function __construct($message, $classname, $methodName, $arguments = null) { parent::__construct($message); $this->classname = $classname; $this->methodName = $methodName; $this->arguments = $arguments; } public function getClassname() { return $this->classname; } public function getMethodName() { return $this->methodName; } public function getArguments() { return $this->arguments; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; use ReflectionClass; class ClassMirrorException extends \RuntimeException implements DoublerException { private $class; public function __construct($message, ReflectionClass $class) { parent::__construct($message); $this->class = $class; } public function getReflectedClass() { return $this->class; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; class ReturnByReferenceException extends DoubleException { private $classname; private $methodName; /** * @param string $message * @param string $classname * @param string $methodName */ public function __construct($message, $classname, $methodName) { parent::__construct($message); $this->classname = $classname; $this->methodName = $methodName; } public function getClassname() { return $this->classname; } public function getMethodName() { return $this->methodName; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Doubler; class InterfaceNotFoundException extends ClassNotFoundException { public function getInterfaceName() { return $this->getClassname(); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception; /** * Core Prophecy exception interface. * All Prophecy exceptions implement it. * * @author Konstantin Kudryashov */ interface Exception { /** * @return string */ public function getMessage(); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception; class InvalidArgumentException extends \InvalidArgumentException implements Exception { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prediction; use Prophecy\Exception\Exception; interface PredictionException extends Exception { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prediction; use Prophecy\Exception\Prophecy\MethodProphecyException; class NoCallsException extends MethodProphecyException implements PredictionException { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prediction; use Prophecy\Prophecy\MethodProphecy; class UnexpectedCallsCountException extends UnexpectedCallsException { private $expectedCount; public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) { parent::__construct($message, $methodProphecy, $calls); $this->expectedCount = intval($count); } public function getExpectedCount() { return $this->expectedCount; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prediction; use RuntimeException; /** * Basic failed prediction exception. * Use it for custom prediction failures. * * @author Konstantin Kudryashov */ class FailedPredictionException extends RuntimeException implements PredictionException { } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prediction; use Prophecy\Prophecy\ObjectProphecy; class AggregateException extends \RuntimeException implements PredictionException { private $exceptions = array(); private $objectProphecy; public function append(PredictionException $exception) { $message = $exception->getMessage(); $message = strtr($message, array("\n" => "\n "))."\n"; $message = empty($this->exceptions) ? $message : "\n" . $message; $this->message = rtrim($this->message.$message); $this->exceptions[] = $exception; } /** * @return PredictionException[] */ public function getExceptions() { return $this->exceptions; } public function setObjectProphecy(ObjectProphecy $objectProphecy) { $this->objectProphecy = $objectProphecy; } /** * @return ObjectProphecy */ public function getObjectProphecy() { return $this->objectProphecy; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Exception\Prediction; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Exception\Prophecy\MethodProphecyException; class UnexpectedCallsException extends MethodProphecyException implements PredictionException { private $calls = array(); public function __construct($message, MethodProphecy $methodProphecy, array $calls) { parent::__construct($message, $methodProphecy); $this->calls = $calls; } public function getCalls() { return $this->calls; } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy; use Prophecy\Doubler\Doubler; use Prophecy\Doubler\LazyDouble; use Prophecy\Doubler\ClassPatch; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\RevealerInterface; use Prophecy\Prophecy\Revealer; use Prophecy\Call\CallCenter; use Prophecy\Util\StringUtil; use Prophecy\Exception\Prediction\PredictionException; use Prophecy\Exception\Prediction\AggregateException; /** * Prophet creates prophecies. * * @author Konstantin Kudryashov */ class Prophet { private $doubler; private $revealer; private $util; /** * @var ObjectProphecy[] */ private $prophecies = array(); /** * Initializes Prophet. * * @param null|Doubler $doubler * @param null|RevealerInterface $revealer * @param null|StringUtil $util */ public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, StringUtil $util = null) { if (null === $doubler) { $doubler = new Doubler; $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch); $doubler->registerClassPatch(new ClassPatch\TraversablePatch); $doubler->registerClassPatch(new ClassPatch\ThrowablePatch); $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch); $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch); $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch); $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); $doubler->registerClassPatch(new ClassPatch\MagicCallPatch); $doubler->registerClassPatch(new ClassPatch\KeywordPatch); } $this->doubler = $doubler; $this->revealer = $revealer ?: new Revealer; $this->util = $util ?: new StringUtil; } /** * Creates new object prophecy. * * @param null|string $classOrInterface Class or interface name * * @return ObjectProphecy */ public function prophesize($classOrInterface = null) { $this->prophecies[] = $prophecy = new ObjectProphecy( new LazyDouble($this->doubler), new CallCenter($this->util), $this->revealer ); if ($classOrInterface && class_exists($classOrInterface)) { return $prophecy->willExtend($classOrInterface); } if ($classOrInterface && interface_exists($classOrInterface)) { return $prophecy->willImplement($classOrInterface); } return $prophecy; } /** * Returns all created object prophecies. * * @return ObjectProphecy[] */ public function getProphecies() { return $this->prophecies; } /** * Returns Doubler instance assigned to this Prophet. * * @return Doubler */ public function getDoubler() { return $this->doubler; } /** * Checks all predictions defined by prophecies of this Prophet. * * @throws Exception\Prediction\AggregateException If any prediction fails */ public function checkPredictions() { $exception = new AggregateException("Some predictions failed:\n"); foreach ($this->prophecies as $prophecy) { try { $prophecy->checkProphecyMethodsPredictions(); } catch (PredictionException $e) { $exception->append($e); } } if (count($exception->getExceptions())) { throw $exception; } } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * This class is a modification from sebastianbergmann/exporter * @see https://github.com/sebastianbergmann/exporter */ class ExportUtil { /** * Exports a value as 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 * @param int $indentation The indentation level of the 2nd+ line * @return string */ public static function export($value, $indentation = 0) { return self::recursiveExport($value, $indentation); } /** * Converts an object to an array containing all of its private, protected * and public properties. * * @param mixed $value * @return array */ public static function toArray($value) { if (!is_object($value)) { return (array) $value; } $array = array(); foreach ((array) $value as $key => $val) { // 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]; } // See https://github.com/php/php-src/commit/5721132 if ($key === "\0gcdata") { continue; } $array[$key] = $val; } // Some internal classes like SplObjectStorage don't work with the // above (fast) mechanism nor with reflection in Zend. // Format the output similarly to print_r() in this case if ($value instanceof \SplObjectStorage) { // However, the fast method does work in HHVM, and exposes the // internal implementation. Hide it again. if (property_exists('\SplObjectStorage', '__storage')) { unset($array['__storage']); } elseif (property_exists('\SplObjectStorage', 'storage')) { unset($array['storage']); } if (property_exists('\SplObjectStorage', '__key')) { unset($array['__key']); } foreach ($value as $key => $val) { $array[spl_object_hash($val)] = array( 'obj' => $val, 'inf' => $value->getInfo(), ); } } return $array; } /** * Recursive implementation of export * * @param mixed $value The value to export * @param int $indentation The indentation level of the 2nd+ line * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects * @return string * @see SebastianBergmann\Exporter\Exporter::export */ protected static function recursiveExport(&$value, $indentation, $processed = null) { if ($value === null) { return 'null'; } if ($value === true) { return 'true'; } if ($value === false) { return 'false'; } if (is_float($value) && floatval(intval($value)) === $value) { return "$value.0"; } if (is_resource($value)) { return sprintf( 'resource(%d) of type (%s)', $value, get_resource_type($value) ); } if (is_string($value)) { // Match for most non printable chars somewhat taking multibyte chars into account if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { return 'Binary String: 0x' . bin2hex($value); } return "'" . str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . "'"; } $whitespace = str_repeat(' ', 4 * $indentation); if (!$processed) { $processed = new Context; } if (is_array($value)) { if (($key = $processed->contains($value)) !== false) { return 'Array &' . $key; } $array = $value; $key = $processed->add($value); $values = ''; if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($value[$k], $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('Array &%s (%s)', $key, $values); } if (is_object($value)) { $class = get_class($value); if ($value instanceof ProphecyInterface) { return sprintf('%s Object (*Prophecy*)', $class); } elseif ($hash = $processed->contains($value)) { return sprintf('%s:%s Object', $class, $hash); } $hash = $processed->add($value); $values = ''; $array = self::toArray($value); if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($v, $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('%s:%s Object (%s)', $class, $hash, $values); } return var_export($value, true); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Util; use Prophecy\Call\Call; /** * String utility. * * @author Konstantin Kudryashov */ class StringUtil { private $verbose; /** * @param bool $verbose */ public function __construct($verbose = true) { $this->verbose = $verbose; } /** * Stringifies any provided value. * * @param mixed $value * @param boolean $exportObject * * @return string */ public function stringify($value, $exportObject = true) { if (is_array($value)) { if (range(0, count($value) - 1) === array_keys($value)) { return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']'; } $stringify = array($this, __FUNCTION__); return '['.implode(', ', array_map(function ($item, $key) use ($stringify) { return (is_integer($key) ? $key : '"'.$key.'"'). ' => '.call_user_func($stringify, $item); }, $value, array_keys($value))).']'; } if (is_resource($value)) { return get_resource_type($value).':'.$value; } if (is_object($value)) { return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value)); } if (true === $value || false === $value) { return $value ? 'true' : 'false'; } if (is_string($value)) { $str = sprintf('"%s"', str_replace("\n", '\\n', $value)); if (!$this->verbose && 50 <= strlen($str)) { return substr($str, 0, 50).'"...'; } return $str; } if (null === $value) { return 'null'; } return (string) $value; } /** * Stringifies provided array of calls. * * @param Call[] $calls Array of Call instances * * @return string */ public function stringifyCalls(array $calls) { $self = $this; return implode(PHP_EOL, array_map(function (Call $call) use ($self) { return sprintf(' - %s(%s) @ %s', $call->getMethodName(), implode(', ', array_map(array($self, 'stringify'), $call->getArguments())), str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace()) ); }, $calls)); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy; use Prophecy\Argument\Token; /** * Argument tokens shortcuts. * * @author Konstantin Kudryashov */ class Argument { /** * Checks that argument is exact value or object. * * @param mixed $value * * @return Token\ExactValueToken */ public static function exact($value) { return new Token\ExactValueToken($value); } /** * Checks that argument is of specific type or instance of specific class. * * @param string $type Type name (`integer`, `string`) or full class name * * @return Token\TypeToken */ public static function type($type) { return new Token\TypeToken($type); } /** * Checks that argument object has specific state. * * @param string $methodName * @param mixed $value * * @return Token\ObjectStateToken */ public static function which($methodName, $value) { return new Token\ObjectStateToken($methodName, $value); } /** * Checks that argument matches provided callback. * * @param callable $callback * * @return Token\CallbackToken */ public static function that($callback) { return new Token\CallbackToken($callback); } /** * Matches any single value. * * @return Token\AnyValueToken */ public static function any() { return new Token\AnyValueToken; } /** * Matches all values to the rest of the signature. * * @return Token\AnyValuesToken */ public static function cetera() { return new Token\AnyValuesToken; } /** * Checks that argument matches all tokens * * @param mixed ... a list of tokens * * @return Token\LogicalAndToken */ public static function allOf() { return new Token\LogicalAndToken(func_get_args()); } /** * Checks that argument array or countable object has exact number of elements. * * @param integer $value array elements count * * @return Token\ArrayCountToken */ public static function size($value) { return new Token\ArrayCountToken($value); } /** * Checks that argument array contains (key, value) pair * * @param mixed $key exact value or token * @param mixed $value exact value or token * * @return Token\ArrayEntryToken */ public static function withEntry($key, $value) { return new Token\ArrayEntryToken($key, $value); } /** * Checks that arguments array entries all match value * * @param mixed $value * * @return Token\ArrayEveryEntryToken */ public static function withEveryEntry($value) { return new Token\ArrayEveryEntryToken($value); } /** * Checks that argument array contains value * * @param mixed $value * * @return Token\ArrayEntryToken */ public static function containing($value) { return new Token\ArrayEntryToken(self::any(), $value); } /** * Checks that argument array has key * * @param mixed $key exact value or token * * @return Token\ArrayEntryToken */ public static function withKey($key) { return new Token\ArrayEntryToken($key, self::any()); } /** * Checks that argument does not match the value|token. * * @param mixed $value either exact value or argument token * * @return Token\LogicalNotToken */ public static function not($value) { return new Token\LogicalNotToken($value); } /** * @param string $value * * @return Token\StringContainsToken */ public static function containingString($value) { return new Token\StringContainsToken($value); } /** * Checks that argument is identical value. * * @param mixed $value * * @return Token\IdenticalValueToken */ public static function is($value) { return new Token\IdenticalValueToken($value); } /** * Check that argument is same value when rounding to the * given precision. * * @param float $value * @param float $precision * * @return Token\ApproximateValueToken */ public static function approximate($value, $precision = 0) { return new Token\ApproximateValueToken($value, $precision); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prediction; use Prophecy\Call\Call; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Util\StringUtil; use Prophecy\Exception\Prediction\UnexpectedCallsException; /** * No calls prediction. * * @author Konstantin Kudryashov */ class NoCallsPrediction implements PredictionInterface { private $util; /** * Initializes prediction. * * @param null|StringUtil $util */ public function __construct(StringUtil $util = null) { $this->util = $util ?: new StringUtil; } /** * Tests that there were no calls made. * * @param Call[] $calls * @param ObjectProphecy $object * @param MethodProphecy $method * * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException */ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { if (!count($calls)) { return; } $verb = count($calls) === 1 ? 'was' : 'were'; throw new UnexpectedCallsException(sprintf( "No calls expected that match:\n". " %s->%s(%s)\n". "but %d %s made:\n%s", get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), count($calls), $verb, $this->util->stringifyCalls($calls) ), $method, $calls); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prediction; use Prophecy\Call\Call; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Argument\ArgumentsWildcard; use Prophecy\Argument\Token\AnyValuesToken; use Prophecy\Util\StringUtil; use Prophecy\Exception\Prediction\UnexpectedCallsCountException; /** * Prediction interface. * Predictions are logical test blocks, tied to `should...` keyword. * * @author Konstantin Kudryashov */ class CallTimesPrediction implements PredictionInterface { private $times; private $util; /** * Initializes prediction. * * @param int $times * @param StringUtil $util */ public function __construct($times, StringUtil $util = null) { $this->times = intval($times); $this->util = $util ?: new StringUtil; } /** * Tests that there was exact amount of calls made. * * @param Call[] $calls * @param ObjectProphecy $object * @param MethodProphecy $method * * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException */ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { if ($this->times == count($calls)) { return; } $methodCalls = $object->findProphecyMethodCalls( $method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken)) ); if (count($calls)) { $message = sprintf( "Expected exactly %d calls that match:\n". " %s->%s(%s)\n". "but %d were made:\n%s", $this->times, get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), count($calls), $this->util->stringifyCalls($calls) ); } elseif (count($methodCalls)) { $message = sprintf( "Expected exactly %d calls that match:\n". " %s->%s(%s)\n". "but none were made.\n". "Recorded `%s(...)` calls:\n%s", $this->times, get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls) ); } else { $message = sprintf( "Expected exactly %d calls that match:\n". " %s->%s(%s)\n". "but none were made.", $this->times, get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard() ); } throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prediction; use Prophecy\Call\Call; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; /** * Prediction interface. * Predictions are logical test blocks, tied to `should...` keyword. * * @author Konstantin Kudryashov */ interface PredictionInterface { /** * Tests that double fulfilled prediction. * * @param Call[] $calls * @param ObjectProphecy $object * @param MethodProphecy $method * * @throws object * @return void */ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prediction; use Prophecy\Call\Call; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Argument\ArgumentsWildcard; use Prophecy\Argument\Token\AnyValuesToken; use Prophecy\Util\StringUtil; use Prophecy\Exception\Prediction\NoCallsException; /** * Call prediction. * * @author Konstantin Kudryashov */ class CallPrediction implements PredictionInterface { private $util; /** * Initializes prediction. * * @param StringUtil $util */ public function __construct(StringUtil $util = null) { $this->util = $util ?: new StringUtil; } /** * Tests that there was at least one call. * * @param Call[] $calls * @param ObjectProphecy $object * @param MethodProphecy $method * * @throws \Prophecy\Exception\Prediction\NoCallsException */ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { if (count($calls)) { return; } $methodCalls = $object->findProphecyMethodCalls( $method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken)) ); if (count($methodCalls)) { throw new NoCallsException(sprintf( "No calls have been made that match:\n". " %s->%s(%s)\n". "but expected at least one.\n". "Recorded `%s(...)` calls:\n%s", get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls) ), $method); } throw new NoCallsException(sprintf( "No calls have been made that match:\n". " %s->%s(%s)\n". "but expected at least one.", get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard() ), $method); } } * Marcello Duarte * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Prophecy\Prediction; use Prophecy\Call\Call; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Exception\InvalidArgumentException; use Closure; /** * Callback prediction. * * @author Konstantin Kudryashov */ class CallbackPrediction implements PredictionInterface { private $callback; /** * Initializes callback prediction. * * @param callable $callback Custom callback * * @throws \Prophecy\Exception\InvalidArgumentException */ public function __construct($callback) { if (!is_callable($callback)) { throw new InvalidArgumentException(sprintf( 'Callable expected as an argument to CallbackPrediction, but got %s.', gettype($callback) )); } $this->callback = $callback; } /** * Executes preset callback. * * @param Call[] $calls * @param ObjectProphecy $object * @param MethodProphecy $method */ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { $callback = $this->callback; if ($callback instanceof Closure && method_exists('Closure', 'bind')) { $callback = Closure::bind($callback, $object); } call_user_func($callback, $calls, $object, $method); } } Copyright (c) 2013 Konstantin Kudryashov Marcello Duarte Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; /** * Thrown when an assertion for string equality failed. */ class ComparisonFailure extends \RuntimeException { /** * Expected value of the retrieval which does not match $actual. * * @var mixed */ protected $expected; /** * Actually retrieved value which does not match $expected. * * @var mixed */ protected $actual; /** * The string representation of the expected value * * @var string */ protected $expectedAsString; /** * The string representation of the actual value * * @var string */ protected $actualAsString; /** * @var bool */ protected $identical; /** * Optional message which is placed in front of the first line * returned by toString(). * * @var string */ protected $message; /** * Initialises with the expected value and the actual value. * * @param mixed $expected expected value retrieved * @param mixed $actual actual value retrieved * @param string $expectedAsString * @param string $actualAsString * @param bool $identical * @param string $message a string which is prefixed on all returned lines * in the difference output */ public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '') { $this->expected = $expected; $this->actual = $actual; $this->expectedAsString = $expectedAsString; $this->actualAsString = $actualAsString; $this->message = $message; } public function getActual() { return $this->actual; } public function getExpected() { return $this->expected; } /** * @return string */ public function getActualAsString() { return $this->actualAsString; } /** * @return string */ public function getExpectedAsString() { return $this->expectedAsString; } /** * @return string */ public function getDiff() { if (!$this->actualAsString && !$this->expectedAsString) { return ''; } $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); return $differ->diff($this->expectedAsString, $this->actualAsString); } /** * @return string */ public function toString() { return $this->message . $this->getDiff(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares arrays for equality. */ class ArrayComparator extends 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 bool */ public function accepts($expected, $actual) { return \is_array($expected) && \is_array($actual); } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * @param array $processed List of already processed elements (used to prevent infinite recursion) * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) { if ($canonicalize) { \sort($expected); \sort($actual); } $remaining = $actual; $actualAsString = "Array (\n"; $expectedAsString = "Array (\n"; $equal = true; foreach ($expected as $key => $value) { unset($remaining[$key]); if (!\array_key_exists($key, $actual)) { $expectedAsString .= \sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value) ); $equal = false; continue; } try { $comparator = $this->factory->getComparatorFor($value, $actual[$key]); $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); $expectedAsString .= \sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value) ); $actualAsString .= \sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($actual[$key]) ); } catch (ComparisonFailure $e) { $expectedAsString .= \sprintf( " %s => %s\n", $this->exporter->export($key), $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected()) ); $actualAsString .= \sprintf( " %s => %s\n", $this->exporter->export($key), $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual()) ); $equal = false; } } foreach ($remaining as $key => $value) { $actualAsString .= \sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value) ); $equal = false; } $expectedAsString .= ')'; $actualAsString .= ')'; if (!$equal) { throw new ComparisonFailure( $expected, $actual, $expectedAsString, $actualAsString, false, 'Failed asserting that two arrays are equal.' ); } } protected function indent($lines) { return \trim(\str_replace("\n", "\n ", $lines)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Factory for comparators which compare values for equality. */ class Factory { /** * @var Factory */ private static $instance; /** * @var Comparator[] */ private $customComparators = []; /** * @var Comparator[] */ private $defaultComparators = []; /** * @return Factory */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self; } return self::$instance; } /** * Constructs a new factory. */ public function __construct() { $this->registerDefaultComparators(); } /** * 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 Comparator */ public function getComparatorFor($expected, $actual) { foreach ($this->customComparators as $comparator) { if ($comparator->accepts($expected, $actual)) { return $comparator; } } foreach ($this->defaultComparators as $comparator) { if ($comparator->accepts($expected, $actual)) { return $comparator; } } } /** * Registers a new comparator. * * This comparator will be returned by getComparatorFor() 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 invoked * before those of the other comparators. * * @param Comparator $comparator The comparator to be registered */ public function register(Comparator $comparator) { \array_unshift($this->customComparators, $comparator); $comparator->setFactory($this); } /** * Unregisters a comparator. * * This comparator will no longer be considered by getComparatorFor(). * * @param Comparator $comparator The comparator to be unregistered */ public function unregister(Comparator $comparator) { foreach ($this->customComparators as $key => $_comparator) { if ($comparator === $_comparator) { unset($this->customComparators[$key]); } } } /** * Unregisters all non-default comparators. */ public function reset() { $this->customComparators = []; } private function registerDefaultComparators() { $this->registerDefaultComparator(new MockObjectComparator); $this->registerDefaultComparator(new DateTimeComparator); $this->registerDefaultComparator(new DOMNodeComparator); $this->registerDefaultComparator(new SplObjectStorageComparator); $this->registerDefaultComparator(new ExceptionComparator); $this->registerDefaultComparator(new ObjectComparator); $this->registerDefaultComparator(new ResourceComparator); $this->registerDefaultComparator(new ArrayComparator); $this->registerDefaultComparator(new DoubleComparator); $this->registerDefaultComparator(new NumericComparator); $this->registerDefaultComparator(new ScalarComparator); $this->registerDefaultComparator(new TypeComparator); } private function registerDefaultComparator(Comparator $comparator) { $this->defaultComparators[] = $comparator; $comparator->setFactory($this); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares values for type equality. */ class TypeComparator extends 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 bool */ public function accepts($expected, $actual) { return true; } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) { if (\gettype($expected) != \gettype($actual)) { throw new ComparisonFailure( $expected, $actual, // we don't need a diff '', '', false, \sprintf( '%s does not match expected type "%s".', $this->exporter->shortenedExport($actual), \gettype($expected) ) ); } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares resources for equality. */ class ResourceComparator extends 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 bool */ public function accepts($expected, $actual) { return \is_resource($expected) && \is_resource($actual); } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) { if ($actual != $expected) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual) ); } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares numerical values for equality. */ class NumericComparator extends ScalarComparator { /** * 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 bool */ public function accepts($expected, $actual) { // all numerical values, but not if one of them is a double // or both of them are strings return \is_numeric($expected) && \is_numeric($actual) && !(\is_float($expected) || \is_float($actual)) && !(\is_string($expected) && \is_string($actual)); } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) { if (\is_infinite($actual) && \is_infinite($expected)) { return; // @codeCoverageIgnore } if ((\is_infinite($actual) xor \is_infinite($expected)) || (\is_nan($actual) || \is_nan($expected)) || \abs($actual - $expected) > $delta) { throw new ComparisonFailure( $expected, $actual, '', '', false, \sprintf( 'Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected) ) ); } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares \SplObjectStorage instances for equality. */ class SplObjectStorageComparator extends 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 bool */ public function accepts($expected, $actual) { return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) { foreach ($actual as $object) { if (!$expected->contains($object)) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), false, 'Failed asserting that two objects are equal.' ); } } foreach ($expected as $object) { if (!$actual->contains($object)) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), false, 'Failed asserting that two objects are equal.' ); } } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use SebastianBergmann\Exporter\Exporter; /** * Abstract base class for comparators which compare values for equality. */ abstract class Comparator { /** * @var Factory */ protected $factory; /** * @var Exporter */ protected $exporter; public function __construct() { $this->exporter = new Exporter; } public function setFactory(Factory $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 bool */ abstract public function accepts($expected, $actual); /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares DateTimeInterface instances for equality. */ class DateTimeComparator extends ObjectComparator { /** * 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 bool */ public function accepts($expected, $actual) { return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * @param array $processed List of already processed elements (used to prevent infinite recursion) * * @throws \Exception * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) { /** @var \DateTimeInterface $expected */ /** @var \DateTimeInterface $actual */ $absDelta = \abs($delta); $delta = new \DateInterval(\sprintf('PT%dS', $absDelta)); $delta->f = $absDelta - \floor($absDelta); $actualClone = (clone $actual) ->setTimezone(new \DateTimeZone('UTC')); $expectedLower = (clone $expected) ->setTimezone(new \DateTimeZone('UTC')) ->sub($delta); $expectedUpper = (clone $expected) ->setTimezone(new \DateTimeZone('UTC')) ->add($delta); if ($actualClone < $expectedLower || $actualClone > $expectedUpper) { throw new ComparisonFailure( $expected, $actual, $this->dateTimeToString($expected), $this->dateTimeToString($actual), false, 'Failed asserting that two DateTime objects are equal.' ); } } /** * Returns an ISO 8601 formatted string representation of a datetime or * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly * initialized. */ private function dateTimeToString(\DateTimeInterface $datetime): string { $string = $datetime->format('Y-m-d\TH:i:s.uO'); return $string ?: 'Invalid DateTimeInterface object'; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares doubles for equality. */ class DoubleComparator extends NumericComparator { /** * 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 bool */ public function accepts($expected, $actual) { return (\is_float($expected) || \is_float($actual)) && \is_numeric($expected) && \is_numeric($actual); } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) { if ($delta == 0) { $delta = self::EPSILON; } parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares Exception instances for equality. */ class ExceptionComparator extends ObjectComparator { /** * 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 bool */ 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'], $array['xdebug_message'] ); return $array; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares scalar or NULL values for equality. */ class ScalarComparator extends 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 bool * * @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 First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.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 && \is_string($expected) && \is_string($actual)) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), false, 'Failed asserting that two strings are equal.' ); } if ($expectedToCompare != $actualToCompare) { throw new ComparisonFailure( $expected, $actual, // no diff is required '', '', false, \sprintf( 'Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected) ) ); } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares objects for equality. */ class ObjectComparator extends ArrayComparator { /** * 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 bool */ public function accepts($expected, $actual) { return \is_object($expected) && \is_object($actual); } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * @param array $processed List of already processed elements (used to prevent infinite recursion) * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) { if (\get_class($actual) !== \get_class($expected)) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), false, \sprintf( '%s is not instance of expected class "%s".', $this->exporter->export($actual), \get_class($expected) ) ); } // don't compare twice to allow for cyclic dependencies if (\in_array([$actual, $expected], $processed, true) || \in_array([$expected, $actual], $processed, true)) { return; } $processed[] = [$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 (ComparisonFailure $e) { throw new 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 $this->exporter->toArray($object); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; /** * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. */ class MockObjectComparator extends ObjectComparator { /** * 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 bool */ public function accepts($expected, $actual) { return ($expected instanceof \PHPUnit_Framework_MockObject_MockObject || $expected instanceof \PHPUnit\Framework\MockObject\MockObject) && ($actual 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; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use DOMDocument; use DOMNode; /** * Compares DOMNode instances for equality. */ class DOMNodeComparator extends ObjectComparator { /** * 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 bool */ public function accepts($expected, $actual) { return $expected instanceof DOMNode && $actual instanceof DOMNode; } /** * Asserts that two values are equal. * * @param mixed $expected First value to compare * @param mixed $actual Second value to compare * @param float $delta Allowed numerical distance between two values to consider them equal * @param bool $canonicalize Arrays are sorted before comparison when set to true * @param bool $ignoreCase Case is ignored when set to true * @param array $processed List of already processed elements (used to prevent infinite recursion) * * @throws ComparisonFailure */ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) { $expectedAsString = $this->nodeToText($expected, true, $ignoreCase); $actualAsString = $this->nodeToText($actual, true, $ignoreCase); if ($expectedAsString !== $actualAsString) { $type = $expected instanceof DOMDocument ? 'documents' : 'nodes'; throw new ComparisonFailure( $expected, $actual, $expectedAsString, $actualAsString, false, \sprintf("Failed asserting that two DOM %s are equal.\n", $type) ); } } /** * Returns the normalized, whitespace-cleaned, and indented textual * representation of a DOMNode. */ private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string { if ($canonicalize) { $document = new DOMDocument; @$document->loadXML($node->C14N()); $node = $document; } $document = $node instanceof DOMDocument ? $node : $node->ownerDocument; $document->formatOutput = true; $document->normalizeDocument(); $text = $node instanceof DOMDocument ? $node->saveXML() : $document->saveXML($node); return $ignoreCase ? \strtolower($text) : $text; } } Comparator Copyright (c) 2002-2018, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. getProperties() does not return private properties from ancestor classes. * * @author muratyaman@gmail.com * @see http://php.net/manual/en/reflectionclass.getproperties.php * * @param ReflectionClass $ref * * @return ReflectionProperty[] */ public static function getProperties(ReflectionClass $ref) { $props = $ref->getProperties(); $propsArr = array(); foreach ($props as $prop) { $propertyName = $prop->getName(); $propsArr[$propertyName] = $prop; } if ($parentClass = $ref->getParentClass()) { $parentPropsArr = self::getProperties($parentClass); foreach ($propsArr as $key => $property) { $parentPropsArr[$key] = $property; } return $parentPropsArr; } return $propsArr; } /** * Retrieves property by name from object and all its ancestors. * * @param object|string $object * @param string $name * * @throws PropertyException * @throws ReflectionException * * @return ReflectionProperty */ public static function getProperty($object, $name) { $reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); if ($reflection->hasProperty($name)) { return $reflection->getProperty($name); } if ($parentClass = $reflection->getParentClass()) { return self::getProperty($parentClass->getName(), $name); } throw new PropertyException( sprintf( 'The class "%s" doesn\'t have a property with the given name: "%s".', is_object($object) ? get_class($object) : $object, $name ) ); } } __load(); } } setAccessible(true); $reflectionProperty->setValue($object, new ArrayCollection()); } } setAccessible(true); $oldCollection = $reflectionProperty->getValue($object); $newCollection = $oldCollection->map( function ($item) use ($objectCopier) { return $objectCopier($item); } ); $reflectionProperty->setValue($object, $newCollection); } } callback = $callable; } /** * Replaces the object property by the result of the callback called with the object property. * * {@inheritdoc} */ public function apply($object, $property, $objectCopier) { $reflectionProperty = ReflectionHelper::getProperty($object, $property); $reflectionProperty->setAccessible(true); $value = call_user_func($this->callback, $reflectionProperty->getValue($object)); $reflectionProperty->setValue($object, $value); } } setAccessible(true); $reflectionProperty->setValue($object, null); } } type = $type; } /** * @param mixed $element * * @return boolean */ public function matches($element) { return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type; } } Filter, 'matcher' => Matcher] pairs. */ private $filters = []; /** * Type Filters to apply. * * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. */ private $typeFilters = []; /** * @var bool */ private $skipUncloneable = false; /** * @var bool */ private $useCloneMethod; /** * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used * instead of the regular deep cloning. */ public function __construct($useCloneMethod = false) { $this->useCloneMethod = $useCloneMethod; $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); } /** * If enabled, will not throw an exception when coming across an uncloneable property. * * @param $skipUncloneable * * @return $this */ public function skipUncloneable($skipUncloneable = true) { $this->skipUncloneable = $skipUncloneable; return $this; } /** * Deep copies the given object. * * @param mixed $object * * @return mixed */ public function copy($object) { $this->hashMap = []; return $this->recursiveCopy($object); } public function addFilter(Filter $filter, Matcher $matcher) { $this->filters[] = [ 'matcher' => $matcher, 'filter' => $filter, ]; } public function prependFilter(Filter $filter, Matcher $matcher) { array_unshift($this->filters, [ 'matcher' => $matcher, 'filter' => $filter, ]); } public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) { $this->typeFilters[] = [ 'matcher' => $matcher, 'filter' => $filter, ]; } private function recursiveCopy($var) { // Matches Type Filter if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { return $filter->apply($var); } // Resource if (is_resource($var)) { return $var; } // Array if (is_array($var)) { return $this->copyArray($var); } // Scalar if (! is_object($var)) { return $var; } // Object return $this->copyObject($var); } /** * Copy an array * @param array $array * @return array */ private function copyArray(array $array) { foreach ($array as $key => $value) { $array[$key] = $this->recursiveCopy($value); } return $array; } /** * Copies an object. * * @param object $object * * @throws CloneException * * @return object */ private function copyObject($object) { $objectHash = spl_object_hash($object); if (isset($this->hashMap[$objectHash])) { return $this->hashMap[$objectHash]; } $reflectedObject = new ReflectionObject($object); $isCloneable = $reflectedObject->isCloneable(); if (false === $isCloneable) { if ($this->skipUncloneable) { $this->hashMap[$objectHash] = $object; return $object; } throw new CloneException( sprintf( 'The class "%s" is not cloneable.', $reflectedObject->getName() ) ); } $newObject = clone $object; $this->hashMap[$objectHash] = $newObject; if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) { return $newObject; } if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) { return $newObject; } foreach (ReflectionHelper::getProperties($reflectedObject) as $property) { $this->copyObjectProperty($newObject, $property); } return $newObject; } private function copyObjectProperty($object, ReflectionProperty $property) { // Ignore static properties if ($property->isStatic()) { return; } // Apply the filters foreach ($this->filters as $item) { /** @var Matcher $matcher */ $matcher = $item['matcher']; /** @var Filter $filter */ $filter = $item['filter']; if ($matcher->matches($object, $property->getName())) { $filter->apply( $object, $property->getName(), function ($object) { return $this->recursiveCopy($object); } ); // If a filter matches, we stop processing this property return; } } $property->setAccessible(true); $propertyValue = $property->getValue($object); // Copy the property $property->setValue($object, $this->recursiveCopy($propertyValue)); } /** * Returns first filter that matches variable, `null` if no such filter found. * * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and * 'matcher' with value of type {@see TypeMatcher} * @param mixed $var * * @return TypeFilter|null */ private function getFirstMatchedTypeFilter(array $filterRecords, $var) { $matched = $this->first( $filterRecords, function (array $record) use ($var) { /* @var TypeMatcher $matcher */ $matcher = $record['matcher']; return $matcher->matches($var); } ); return isset($matched) ? $matched['filter'] : null; } /** * Returns first element that matches predicate, `null` if no such element found. * * @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs. * @param callable $predicate Predicate arguments are: element. * * @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher' * with value of type {@see TypeMatcher} or `null`. */ private function first(array $elements, callable $predicate) { foreach ($elements as $element) { if (call_user_func($predicate, $element)) { return $element; } } return null; } } $propertyValue) { $copy->{$propertyName} = $propertyValue; } return $copy; } } copier = $copier; } /** * {@inheritdoc} */ public function apply($element) { $newElement = clone $element; $copy = $this->createCopyClosure(); return $copy($newElement); } private function createCopyClosure() { $copier = $this->copier; $copy = function (SplDoublyLinkedList $list) use ($copier) { // Replace each element in the list with a deep copy of itself for ($i = 1; $i <= $list->count(); $i++) { $copy = $copier->recursiveCopy($list->shift()); $list->push($copy); } return $list; }; return Closure::bind($copy, null, DeepCopy::class); } } callback = $callable; } /** * {@inheritdoc} */ public function apply($element) { return call_user_func($this->callback, $element); } } property = $property; } /** * Matches a property by its name. * * {@inheritdoc} */ public function matches($object, $property) { return $property == $this->property; } } propertyType = $propertyType; } /** * {@inheritdoc} */ public function matches($object, $property) { try { $reflectionProperty = ReflectionHelper::getProperty($object, $property); } catch (ReflectionException $exception) { return false; } $reflectionProperty->setAccessible(true); return $reflectionProperty->getValue($object) instanceof $this->propertyType; } } class = $class; $this->property = $property; } /** * Matches a specific property of a specific class. * * {@inheritdoc} */ public function matches($object, $property) { return ($object instanceof $this->class) && $property == $this->property; } } copy($value); } } The MIT License (MIT) Copyright (c) 2013 My C-Sense Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A simple template engine. * * @since Class available since Release 1.0.0 */ class Text_Template { /** * @var string */ protected $template = ''; /** * @var string */ protected $openDelimiter = '{'; /** * @var string */ protected $closeDelimiter = '}'; /** * @var array */ protected $values = array(); /** * Constructor. * * @param string $file * @throws InvalidArgumentException */ public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}') { $this->setFile($file); $this->openDelimiter = $openDelimiter; $this->closeDelimiter = $closeDelimiter; } /** * Sets the template file. * * @param string $file * @throws InvalidArgumentException */ public function setFile($file) { $distFile = $file . '.dist'; if (file_exists($file)) { $this->template = file_get_contents($file); } else if (file_exists($distFile)) { $this->template = file_get_contents($distFile); } else { throw new InvalidArgumentException( 'Template file could not be loaded.' ); } } /** * Sets one or more template variables. * * @param array $values * @param bool $merge */ public function setVar(array $values, $merge = TRUE) { if (!$merge || empty($this->values)) { $this->values = $values; } else { $this->values = array_merge($this->values, $values); } } /** * Renders the template and returns the result. * * @return string */ public function render() { $keys = array(); foreach ($this->values as $key => $value) { $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; } return str_replace($keys, $this->values, $this->template); } /** * Renders the template and writes the result to a file. * * @param string $target */ public function renderTo($target) { $fp = @fopen($target, 'wt'); if ($fp) { fwrite($fp, $this->render()); fclose($fp); } else { $error = error_get_last(); throw new RuntimeException( sprintf( 'Could not write to %s: %s', $target, substr( $error['message'], strpos($error['message'], ':') + 2 ) ) ); } } } Text_Template Copyright (c) 2009-2015, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; /** * Restorer of snapshots of global state. */ class Restorer { /** * Deletes function definitions that are not defined in a snapshot. * * @throws RuntimeException when the uopz_delete() function is not available * * @see https://github.com/krakjoe/uopz */ public function restoreFunctions(Snapshot $snapshot): void { if (!\function_exists('uopz_delete')) { throw new RuntimeException('The uopz_delete() function is required for this operation'); } $functions = \get_defined_functions(); foreach (\array_diff($functions['user'], $snapshot->functions()) as $function) { uopz_delete($function); } } /** * Restores all global and super-global variables from a snapshot. */ public function restoreGlobalVariables(Snapshot $snapshot): void { $superGlobalArrays = $snapshot->superGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); } $globalVariables = $snapshot->globalVariables(); foreach (\array_keys($GLOBALS) as $key) { if ($key !== 'GLOBALS' && !\in_array($key, $superGlobalArrays) && !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { if (\array_key_exists($key, $globalVariables)) { $GLOBALS[$key] = $globalVariables[$key]; } else { unset($GLOBALS[$key]); } } } } /** * Restores all static attributes in user-defined classes from this snapshot. */ public function restoreStaticAttributes(Snapshot $snapshot): void { $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false); $newClasses = \array_diff($current->classes(), $snapshot->classes()); unset($current); foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { foreach ($staticAttributes as $name => $value) { $reflector = new \ReflectionProperty($className, $name); $reflector->setAccessible(true); $reflector->setValue($value); } } foreach ($newClasses as $className) { $class = new \ReflectionClass($className); $defaults = $class->getDefaultProperties(); foreach ($class->getProperties() as $attribute) { if (!$attribute->isStatic()) { continue; } $name = $attribute->getName(); if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) { continue; } if (!isset($defaults[$name])) { continue; } $attribute->setAccessible(true); $attribute->setValue($defaults[$name]); } } } /** * Restores a super-global variable array from this snapshot. */ private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray): void { $superGlobalVariables = $snapshot->superGlobalVariables(); if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray]) && isset($superGlobalVariables[$superGlobalArray])) { $keys = \array_keys( \array_merge( $GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray] ) ); foreach ($keys as $key) { if (isset($superGlobalVariables[$superGlobalArray][$key])) { $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; } else { unset($GLOBALS[$superGlobalArray][$key]); } } } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; interface Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; final class RuntimeException extends \RuntimeException implements Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; /** * Exports parts of a Snapshot as PHP code. */ final class CodeExporter { public function constants(Snapshot $snapshot): string { $result = ''; foreach ($snapshot->constants() as $name => $value) { $result .= \sprintf( 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value) ); } return $result; } public function globalVariables(Snapshot $snapshot): string { $result = '$GLOBALS = [];' . \PHP_EOL; foreach ($snapshot->globalVariables() as $name => $value) { $result .= \sprintf( '$GLOBALS[%s] = %s;' . \PHP_EOL, $this->exportVariable($name), $this->exportVariable($value) ); } return $result; } public function iniSettings(Snapshot $snapshot): string { $result = ''; foreach ($snapshot->iniSettings() as $key => $value) { $result .= \sprintf( '@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value) ); } return $result; } private function exportVariable($variable): string { if (\is_scalar($variable) || null === $variable || (\is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { return \var_export($variable, true); } return 'unserialize(' . \var_export(\serialize($variable), true) . ')'; } private function arrayOnlyContainsScalars(array $array): bool { $result = true; foreach ($array as $element) { if (\is_array($element)) { $result = $this->arrayOnlyContainsScalars($element); } elseif (!\is_scalar($element) && null !== $element) { $result = false; } if ($result === false) { break; } } return $result; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use SebastianBergmann\ObjectReflector\ObjectReflector; use SebastianBergmann\RecursionContext\Context; /** * A snapshot of global state. */ class Snapshot { /** * @var Blacklist */ private $blacklist; /** * @var array */ private $globalVariables = []; /** * @var array */ private $superGlobalArrays = []; /** * @var array */ private $superGlobalVariables = []; /** * @var array */ private $staticAttributes = []; /** * @var array */ private $iniSettings = []; /** * @var array */ private $includedFiles = []; /** * @var array */ private $constants = []; /** * @var array */ private $functions = []; /** * @var array */ private $interfaces = []; /** * @var array */ private $classes = []; /** * @var array */ private $traits = []; /** * Creates a snapshot of the current global state. */ public function __construct(Blacklist $blacklist = null, bool $includeGlobalVariables = true, bool $includeStaticAttributes = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true) { if ($blacklist === null) { $blacklist = new Blacklist; } $this->blacklist = $blacklist; if ($includeConstants) { $this->snapshotConstants(); } if ($includeFunctions) { $this->snapshotFunctions(); } if ($includeClasses || $includeStaticAttributes) { $this->snapshotClasses(); } if ($includeInterfaces) { $this->snapshotInterfaces(); } if ($includeGlobalVariables) { $this->setupSuperGlobalArrays(); $this->snapshotGlobals(); } if ($includeStaticAttributes) { $this->snapshotStaticAttributes(); } if ($includeIniSettings) { $this->iniSettings = \ini_get_all(null, false); } if ($includeIncludedFiles) { $this->includedFiles = \get_included_files(); } $this->traits = \get_declared_traits(); } public function blacklist(): Blacklist { return $this->blacklist; } public function globalVariables(): array { return $this->globalVariables; } public function superGlobalVariables(): array { return $this->superGlobalVariables; } public function superGlobalArrays(): array { return $this->superGlobalArrays; } public function staticAttributes(): array { return $this->staticAttributes; } public function iniSettings(): array { return $this->iniSettings; } public function includedFiles(): array { return $this->includedFiles; } public function constants(): array { return $this->constants; } public function functions(): array { return $this->functions; } public function interfaces(): array { return $this->interfaces; } public function classes(): array { return $this->classes; } public function traits(): array { return $this->traits; } /** * Creates a snapshot user-defined constants. */ private function snapshotConstants(): void { $constants = \get_defined_constants(true); if (isset($constants['user'])) { $this->constants = $constants['user']; } } /** * Creates a snapshot user-defined functions. */ private function snapshotFunctions(): void { $functions = \get_defined_functions(); $this->functions = $functions['user']; } /** * Creates a snapshot user-defined classes. */ private function snapshotClasses(): void { foreach (\array_reverse(\get_declared_classes()) as $className) { $class = new \ReflectionClass($className); if (!$class->isUserDefined()) { break; } $this->classes[] = $className; } $this->classes = \array_reverse($this->classes); } /** * Creates a snapshot user-defined interfaces. */ private function snapshotInterfaces(): void { foreach (\array_reverse(\get_declared_interfaces()) as $interfaceName) { $class = new \ReflectionClass($interfaceName); if (!$class->isUserDefined()) { break; } $this->interfaces[] = $interfaceName; } $this->interfaces = \array_reverse($this->interfaces); } /** * Creates a snapshot of all global and super-global variables. */ private function snapshotGlobals(): void { $superGlobalArrays = $this->superGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { $this->snapshotSuperGlobalArray($superGlobalArray); } foreach (\array_keys($GLOBALS) as $key) { if ($key !== 'GLOBALS' && !\in_array($key, $superGlobalArrays) && $this->canBeSerialized($GLOBALS[$key]) && !$this->blacklist->isGlobalVariableBlacklisted($key)) { /* @noinspection UnserializeExploitsInspection */ $this->globalVariables[$key] = \unserialize(\serialize($GLOBALS[$key])); } } } /** * Creates a snapshot a super-global variable array. */ private function snapshotSuperGlobalArray(string $superGlobalArray): void { $this->superGlobalVariables[$superGlobalArray] = []; if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { foreach ($GLOBALS[$superGlobalArray] as $key => $value) { /* @noinspection UnserializeExploitsInspection */ $this->superGlobalVariables[$superGlobalArray][$key] = \unserialize(\serialize($value)); } } } /** * Creates a snapshot of all static attributes in user-defined classes. */ private function snapshotStaticAttributes(): void { foreach ($this->classes as $className) { $class = new \ReflectionClass($className); $snapshot = []; foreach ($class->getProperties() as $attribute) { if ($attribute->isStatic()) { $name = $attribute->getName(); if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { continue; } $attribute->setAccessible(true); $value = $attribute->getValue(); if ($this->canBeSerialized($value)) { /* @noinspection UnserializeExploitsInspection */ $snapshot[$name] = \unserialize(\serialize($value)); } } } if (!empty($snapshot)) { $this->staticAttributes[$className] = $snapshot; } } } /** * Returns a list of all super-global variable arrays. */ private function setupSuperGlobalArrays(): void { $this->superGlobalArrays = [ '_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', ]; } private function canBeSerialized($variable): bool { if (\is_scalar($variable) || $variable === null) { return true; } if (\is_resource($variable)) { return false; } foreach ($this->enumerateObjectsAndResources($variable) as $value) { if (\is_resource($value)) { return false; } if (\is_object($value)) { $class = new \ReflectionClass($value); if ($class->isAnonymous()) { return false; } try { @\serialize($value); } catch (\Throwable $t) { return false; } } } return true; } private function enumerateObjectsAndResources($variable): array { if (isset(\func_get_args()[1])) { $processed = \func_get_args()[1]; } else { $processed = new Context; } $result = []; if ($processed->contains($variable)) { return $result; } $array = $variable; $processed->add($variable); if (\is_array($variable)) { foreach ($array as $element) { if (!\is_array($element) && !\is_object($element) && !\is_resource($element)) { continue; } if (!\is_resource($element)) { /** @noinspection SlowArrayOperationsInLoopInspection */ $result = \array_merge( $result, $this->enumerateObjectsAndResources($element, $processed) ); } else { $result[] = $element; } } } else { $result[] = $variable; foreach ((new ObjectReflector)->getAttributes($variable) as $value) { if (!\is_array($value) && !\is_object($value) && !\is_resource($value)) { continue; } if (!\is_resource($value)) { /** @noinspection SlowArrayOperationsInLoopInspection */ $result = \array_merge( $result, $this->enumerateObjectsAndResources($value, $processed) ); } else { $result[] = $value; } } } return $result; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; /** * A blacklist for global state elements that should not be snapshotted. */ final class Blacklist { /** * @var array */ private $globalVariables = []; /** * @var string[] */ private $classes = []; /** * @var string[] */ private $classNamePrefixes = []; /** * @var string[] */ private $parentClasses = []; /** * @var string[] */ private $interfaces = []; /** * @var array */ private $staticAttributes = []; public function addGlobalVariable(string $variableName): void { $this->globalVariables[$variableName] = true; } public function addClass(string $className): void { $this->classes[] = $className; } public function addSubclassesOf(string $className): void { $this->parentClasses[] = $className; } public function addImplementorsOf(string $interfaceName): void { $this->interfaces[] = $interfaceName; } public function addClassNamePrefix(string $classNamePrefix): void { $this->classNamePrefixes[] = $classNamePrefix; } public function addStaticAttribute(string $className, string $attributeName): void { if (!isset($this->staticAttributes[$className])) { $this->staticAttributes[$className] = []; } $this->staticAttributes[$className][$attributeName] = true; } public function isGlobalVariableBlacklisted(string $variableName): bool { return isset($this->globalVariables[$variableName]); } public function isStaticAttributeBlacklisted(string $className, string $attributeName): bool { if (\in_array($className, $this->classes)) { return true; } foreach ($this->classNamePrefixes as $prefix) { if (\strpos($className, $prefix) === 0) { return true; } } $class = new \ReflectionClass($className); foreach ($this->parentClasses as $type) { if ($class->isSubclassOf($type)) { return true; } } foreach ($this->interfaces as $type) { if ($class->implementsInterface($type)) { return true; } } if (isset($this->staticAttributes[$className][$attributeName])) { return true; } return false; } } sebastian/global-state Copyright (c) 2001-2019, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. buildAndCacheFromFactory($className); } /** * Builds the requested object and caches it in static properties for performance * * @return object */ private function buildAndCacheFromFactory(string $className) { $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); $instance = $factory(); if ($this->isSafeToClone(new ReflectionClass($instance))) { self::$cachedCloneables[$className] = clone $instance; } return $instance; } /** * Builds a callable capable of instantiating the given $className without * invoking its constructor. * * @throws InvalidArgumentException * @throws UnexpectedValueException * @throws ReflectionException */ private function buildFactory(string $className) : callable { $reflectionClass = $this->getReflectionClass($className); if ($this->isInstantiableViaReflection($reflectionClass)) { return [$reflectionClass, 'newInstanceWithoutConstructor']; } $serializedString = sprintf( '%s:%d:"%s":0:{}', self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, strlen($className), $className ); $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); return static function () use ($serializedString) { return unserialize($serializedString); }; } /** * @param string $className * * @throws InvalidArgumentException * @throws ReflectionException */ private function getReflectionClass($className) : ReflectionClass { if (! class_exists($className)) { throw InvalidArgumentException::fromNonExistingClass($className); } $reflection = new ReflectionClass($className); if ($reflection->isAbstract()) { throw InvalidArgumentException::fromAbstractClass($reflection); } return $reflection; } /** * @throws UnexpectedValueException */ private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, string $serializedString) : void { set_error_handler(static function ($code, $message, $file, $line) use ($reflectionClass, & $error) : void { $error = UnexpectedValueException::fromUncleanUnSerialization( $reflectionClass, $message, $code, $file, $line ); }); try { $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); } finally { restore_error_handler(); } if ($error) { throw $error; } } /** * @throws UnexpectedValueException */ private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, string $serializedString) : void { try { unserialize($serializedString); } catch (Exception $exception) { throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); } } private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool { return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); } /** * Verifies whether the given class is to be considered internal */ private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool { do { if ($reflectionClass->isInternal()) { return true; } $reflectionClass = $reflectionClass->getParentClass(); } while ($reflectionClass); return false; } /** * Checks if a class is cloneable * * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. */ private function isSafeToClone(ReflectionClass $reflection) : bool { return $reflection->isCloneable() && ! $reflection->hasMethod('__clone'); } } = 50400 && trait_exists($className)) { return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); } return new self(sprintf('The provided class "%s" does not exist', $className)); } public static function fromAbstractClass(ReflectionClass $reflectionClass) : self { return new self(sprintf( 'The provided class "%s" is abstract, and can not be instantiated', $reflectionClass->getName() )); } } getName() ), 0, $exception ); } public static function fromUncleanUnSerialization( ReflectionClass $reflectionClass, string $errorString, int $errorCode, string $errorFile, int $errorLine ) : self { return new self( sprintf( 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' . 'in file "%s" at line "%d"', $reflectionClass->getName(), $errorFile, $errorLine ), 0, new Exception($errorString, $errorCode) ); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; use phpDocumentor\Reflection\DocBlock\Tag; use Webmozart\Assert\Assert; final class DocBlock { /** @var string The opening line for this docblock. */ private $summary = ''; /** @var DocBlock\Description The actual description for this docblock. */ private $description = null; /** @var Tag[] An array containing all the tags in this docblock; except inline. */ private $tags = []; /** @var Types\Context Information about the context of this DocBlock. */ private $context = null; /** @var Location Information about the location of this DocBlock. */ private $location = null; /** @var bool Is this DocBlock (the start of) a template? */ private $isTemplateStart = false; /** @var bool Does this DocBlock signify the end of a DocBlock template? */ private $isTemplateEnd = false; /** * @param string $summary * @param DocBlock\Description $description * @param DocBlock\Tag[] $tags * @param Types\Context $context The context in which the DocBlock occurs. * @param Location $location The location within the file that this DocBlock occurs in. * @param bool $isTemplateStart * @param bool $isTemplateEnd */ public function __construct( $summary = '', DocBlock\Description $description = null, array $tags = [], Types\Context $context = null, Location $location = null, $isTemplateStart = false, $isTemplateEnd = false ) { Assert::string($summary); Assert::boolean($isTemplateStart); Assert::boolean($isTemplateEnd); Assert::allIsInstanceOf($tags, Tag::class); $this->summary = $summary; $this->description = $description ?: new DocBlock\Description(''); foreach ($tags as $tag) { $this->addTag($tag); } $this->context = $context; $this->location = $location; $this->isTemplateEnd = $isTemplateEnd; $this->isTemplateStart = $isTemplateStart; } /** * @return string */ public function getSummary() { return $this->summary; } /** * @return DocBlock\Description */ public function getDescription() { return $this->description; } /** * Returns the current context. * * @return Types\Context */ public function getContext() { return $this->context; } /** * Returns the current location. * * @return Location */ public function getLocation() { return $this->location; } /** * Returns whether this DocBlock is the start of a Template section. * * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. * * An example of such an opening is: * * ``` * /**#@+ * * My DocBlock * * / * ``` * * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). * * @see self::isTemplateEnd() for the check whether a closing marker was provided. * * @return boolean */ public function isTemplateStart() { return $this->isTemplateStart; } /** * Returns whether this DocBlock is the end of a Template section. * * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. * * @return boolean */ public function isTemplateEnd() { return $this->isTemplateEnd; } /** * Returns the tags for this DocBlock. * * @return Tag[] */ public function getTags() { return $this->tags; } /** * Returns an array of tags matching the given name. If no tags are found * an empty array is returned. * * @param string $name String to search by. * * @return Tag[] */ public function getTagsByName($name) { Assert::string($name); $result = []; /** @var Tag $tag */ foreach ($this->getTags() as $tag) { if ($tag->getName() !== $name) { continue; } $result[] = $tag; } return $result; } /** * Checks if a tag of a certain type is present in this DocBlock. * * @param string $name Tag name to check for. * * @return bool */ public function hasTag($name) { Assert::string($name); /** @var Tag $tag */ foreach ($this->getTags() as $tag) { if ($tag->getName() === $name) { return true; } } return false; } /** * Remove a tag from this DocBlock. * * @param Tag $tag The tag to remove. * * @return void */ public function removeTag(Tag $tagToRemove) { foreach ($this->tags as $key => $tag) { if ($tag === $tagToRemove) { unset($this->tags[$key]); break; } } } /** * Adds a tag to this DocBlock. * * @param Tag $tag The tag to add. * * @return void */ private function addTag(Tag $tag) { $this->tags[] = $tag; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Tags\Formatter; use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; use Webmozart\Assert\Assert; /** * Object representing to description for a DocBlock. * * A Description object can consist of plain text but can also include tags. A Description Formatter can then combine * a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete * description text using the format that you would prefer. * * Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is * thus recommended to use that to create a Description object, like this: * * $description = $descriptionFactory->create('This is a {@see Description}', $context); * * The description factory will interpret the given body and create a body template and list of tags from them, and pass * that onto the constructor if this class. * * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial * > type names and FQSENs. * * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this: * * $description = new Description( * 'This is a %1$s', * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ] * ); * * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object * is mainly responsible for rendering. * * @see DescriptionFactory to create a new Description. * @see Description\Formatter for the formatting of the body and tags. */ class Description { /** @var string */ private $bodyTemplate; /** @var Tag[] */ private $tags; /** * Initializes a Description with its body (template) and a listing of the tags used in the body template. * * @param string $bodyTemplate * @param Tag[] $tags */ public function __construct($bodyTemplate, array $tags = []) { Assert::string($bodyTemplate); $this->bodyTemplate = $bodyTemplate; $this->tags = $tags; } /** * Returns the tags for this DocBlock. * * @return Tag[] */ public function getTags() { return $this->tags; } /** * Renders this description as a string where the provided formatter will format the tags in the expected string * format. * * @param Formatter|null $formatter * * @return string */ public function render(Formatter $formatter = null) { if ($formatter === null) { $formatter = new PassthroughFormatter(); } $tags = []; foreach ($this->tags as $tag) { $tags[] = '{' . $formatter->format($tag) . '}'; } return vsprintf($this->bodyTemplate, $tags); } /** * Returns a plain string representation of this description. * * @return string */ public function __toString() { return $this->render(); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; /** * Interface for references in {@see phpDocumentor\Reflection\DocBlock\Tags\See} */ interface Reference { public function __toString(); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; use Webmozart\Assert\Assert; /** * Url reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See} */ final class Url implements Reference { /** * @var string */ private $uri; /** * Url constructor. */ public function __construct($uri) { Assert::stringNotEmpty($uri); $this->uri = $uri; } public function __toString() { return $this->uri; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; use phpDocumentor\Reflection\Fqsen as RealFqsen; /** * Fqsen reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See} */ final class Fqsen implements Reference { /** * @var RealFqsen */ private $fqsen; /** * Fqsen constructor. */ public function __construct(RealFqsen $fqsen) { $this->fqsen = $fqsen; } /** * @return string string representation of the referenced fqsen */ public function __toString() { return (string)$this->fqsen; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}property-read tag in a Docblock. */ class PropertyRead extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'property-read'; /** @var Type */ private $type; /** @var string */ protected $variableName = ''; /** * @param string $variableName * @param Type $type * @param Description $description */ public function __construct($variableName, Type $type = null, Description $description = null) { Assert::string($variableName); $this->variableName = $variableName; $this->type = $type; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::stringNotEmpty($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); $type = null; $variableName = ''; // if the first item that is encountered is not a variable; it is a type if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { $type = $typeResolver->resolve(array_shift($parts), $context); array_shift($parts); } // if the next item starts with a $ or ...$ it must be the variable name if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { $variableName = array_shift($parts); array_shift($parts); if (substr($variableName, 0, 1) === '$') { $variableName = substr($variableName, 1); } } $description = $descriptionFactory->create(implode('', $parts), $context); return new static($variableName, $type, $description); } /** * Returns the variable's name. * * @return string */ public function getVariableName() { return $this->variableName; } /** * Returns the variable's type or null if unknown. * * @return Type|null */ public function getType() { return $this->type; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return ($this->type ? $this->type . ' ' : '') . '$' . $this->variableName . ($this->description ? ' ' . $this->description : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}return tag in a Docblock. */ final class Return_ extends BaseTag implements Factory\StaticMethod { protected $name = 'return'; /** @var Type */ private $type; public function __construct(Type $type, Description $description = null) { $this->type = $type; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::string($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/\s+/Su', $body, 2); $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context); $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context); return new static($type, $description); } /** * Returns the type section of the variable. * * @return Type */ public function getType() { return $this->type; } public function __toString() { return $this->type . ' ' . $this->description; } } * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}version tag in a Docblock. */ final class Version extends BaseTag implements Factory\StaticMethod { protected $name = 'version'; /** * PCRE regular expression matching a version vector. * Assumes the "x" modifier. */ const REGEX_VECTOR = '(?: # Normal release vectors. \d\S* | # VCS version vectors. Per PHPCS, they are expected to # follow the form of the VCS name, followed by ":", followed # by the version vector itself. # By convention, popular VCSes like CVS, SVN and GIT use "$" # around the actual version vector. [^\s\:]+\:\s*\$[^\$]+\$ )'; /** @var string The version vector. */ private $version = ''; public function __construct($version = null, Description $description = null) { Assert::nullOrStringNotEmpty($version); $this->version = $version; $this->description = $description; } /** * @return static */ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) { Assert::nullOrString($body); if (empty($body)) { return new static(); } $matches = []; if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { return null; } return new static( $matches[1], $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) ); } /** * Gets the version section of the tag. * * @return string */ public function getVersion() { return $this->version; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return $this->version . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tags\Formatter; class PassthroughFormatter implements Formatter { /** * Formats the given tag to return a simple plain text version. * * @param Tag $tag * * @return string */ public function format(Tag $tag) { return trim('@' . $tag->getName() . ' ' . (string)$tag); } } * @copyright 2017 Mike van Riel * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tags\Formatter; class AlignFormatter implements Formatter { /** @var int The maximum tag name length. */ protected $maxLen = 0; /** * Constructor. * * @param Tag[] $tags All tags that should later be aligned with the formatter. */ public function __construct(array $tags) { foreach ($tags as $tag) { $this->maxLen = max($this->maxLen, strlen($tag->getName())); } } /** * Formats the given tag to return a simple plain text version. * * @param Tag $tag * * @return string */ public function format(Tag $tag) { return '@' . $tag->getName() . str_repeat(' ', $this->maxLen - strlen($tag->getName()) + 1) . (string)$tag; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}property tag in a Docblock. */ class Property extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'property'; /** @var Type */ private $type; /** @var string */ protected $variableName = ''; /** * @param string $variableName * @param Type $type * @param Description $description */ public function __construct($variableName, Type $type = null, Description $description = null) { Assert::string($variableName); $this->variableName = $variableName; $this->type = $type; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::stringNotEmpty($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); $type = null; $variableName = ''; // if the first item that is encountered is not a variable; it is a type if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { $type = $typeResolver->resolve(array_shift($parts), $context); array_shift($parts); } // if the next item starts with a $ or ...$ it must be the variable name if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { $variableName = array_shift($parts); array_shift($parts); if (substr($variableName, 0, 1) === '$') { $variableName = substr($variableName, 1); } } $description = $descriptionFactory->create(implode('', $parts), $context); return new static($variableName, $type, $description); } /** * Returns the variable's name. * * @return string */ public function getVariableName() { return $this->variableName; } /** * Returns the variable's type or null if unknown. * * @return Type|null */ public function getType() { return $this->type; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return ($this->type ? $this->type . ' ' : '') . '$' . $this->variableName . ($this->description ? ' ' . $this->description : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}source tag in a Docblock. */ final class Source extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'source'; /** @var int The starting line, relative to the structural element's location. */ private $startingLine = 1; /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */ private $lineCount = null; public function __construct($startingLine, $lineCount = null, Description $description = null) { Assert::integerish($startingLine); Assert::nullOrIntegerish($lineCount); $this->startingLine = (int)$startingLine; $this->lineCount = $lineCount !== null ? (int)$lineCount : null; $this->description = $description; } /** * {@inheritdoc} */ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) { Assert::stringNotEmpty($body); Assert::notNull($descriptionFactory); $startingLine = 1; $lineCount = null; $description = null; // Starting line / Number of lines / Description if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) { $startingLine = (int)$matches[1]; if (isset($matches[2]) && $matches[2] !== '') { $lineCount = (int)$matches[2]; } $description = $matches[3]; } return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context)); } /** * Gets the starting line. * * @return int The starting line, relative to the structural element's * location. */ public function getStartingLine() { return $this->startingLine; } /** * Returns the number of lines. * * @return int|null The number of lines, relative to the starting line. NULL * means "to the end". */ public function getLineCount() { return $this->lineCount; } public function __toString() { return $this->startingLine . ($this->lineCount !== null ? ' ' . $this->lineCount : '') . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use Webmozart\Assert\Assert; /** * Reflection class for an {@}author tag in a Docblock. */ final class Author extends BaseTag implements Factory\StaticMethod { /** @var string register that this is the author tag. */ protected $name = 'author'; /** @var string The name of the author */ private $authorName = ''; /** @var string The email of the author */ private $authorEmail = ''; /** * Initializes this tag with the author name and e-mail. * * @param string $authorName * @param string $authorEmail */ public function __construct($authorName, $authorEmail) { Assert::string($authorName); Assert::string($authorEmail); if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) { throw new \InvalidArgumentException('The author tag does not have a valid e-mail address'); } $this->authorName = $authorName; $this->authorEmail = $authorEmail; } /** * Gets the author's name. * * @return string The author's name. */ public function getAuthorName() { return $this->authorName; } /** * Returns the author's email. * * @return string The author's email. */ public function getEmail() { return $this->authorEmail; } /** * Returns this tag in string form. * * @return string */ public function __toString() { return $this->authorName . (strlen($this->authorEmail) ? ' <' . $this->authorEmail . '>' : ''); } /** * Attempts to create a new Author object based on †he tag body. * * @param string $body * * @return static */ public static function create($body) { Assert::string($body); $splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches); if (!$splitTagContent) { return null; } $authorName = trim($matches[1]); $email = isset($matches[2]) ? trim($matches[2]) : ''; return new static($authorName, $email); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}var tag in a Docblock. */ class Var_ extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'var'; /** @var Type */ private $type; /** @var string */ protected $variableName = ''; /** * @param string $variableName * @param Type $type * @param Description $description */ public function __construct($variableName, Type $type = null, Description $description = null) { Assert::string($variableName); $this->variableName = $variableName; $this->type = $type; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::stringNotEmpty($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); $type = null; $variableName = ''; // if the first item that is encountered is not a variable; it is a type if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { $type = $typeResolver->resolve(array_shift($parts), $context); array_shift($parts); } // if the next item starts with a $ or ...$ it must be the variable name if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { $variableName = array_shift($parts); array_shift($parts); if (substr($variableName, 0, 1) === '$') { $variableName = substr($variableName, 1); } } $description = $descriptionFactory->create(implode('', $parts), $context); return new static($variableName, $type, $description); } /** * Returns the variable's name. * * @return string */ public function getVariableName() { return $this->variableName; } /** * Returns the variable's type or null if unknown. * * @return Type|null */ public function getType() { return $this->type; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return ($this->type ? $this->type . ' ' : '') . (empty($this->variableName) ? null : ('$' . $this->variableName)) . ($this->description ? ' ' . $this->description : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\Tag; use Webmozart\Assert\Assert; /** * Reflection class for a {@}example tag in a Docblock. */ final class Example extends BaseTag { /** * @var string Path to a file to use as an example. May also be an absolute URI. */ private $filePath; /** * @var bool Whether the file path component represents an URI. This determines how the file portion * appears at {@link getContent()}. */ private $isURI = false; /** * @var int */ private $startingLine; /** * @var int */ private $lineCount; public function __construct($filePath, $isURI, $startingLine, $lineCount, $description) { Assert::notEmpty($filePath); Assert::integer($startingLine); Assert::greaterThanEq($startingLine, 0); $this->filePath = $filePath; $this->startingLine = $startingLine; $this->lineCount = $lineCount; $this->name = 'example'; if ($description !== null) { $this->description = trim($description); } $this->isURI = $isURI; } /** * {@inheritdoc} */ public function getContent() { if (null === $this->description) { $filePath = '"' . $this->filePath . '"'; if ($this->isURI) { $filePath = $this->isUriRelative($this->filePath) ? str_replace('%2F', '/', rawurlencode($this->filePath)) :$this->filePath; } return trim($filePath . ' ' . parent::getDescription()); } return $this->description; } /** * {@inheritdoc} */ public static function create($body) { // File component: File path in quotes or File URI / Source information if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) { return null; } $filePath = null; $fileUri = null; if ('' !== $matches[1]) { $filePath = $matches[1]; } else { $fileUri = $matches[2]; } $startingLine = 1; $lineCount = null; $description = null; if (array_key_exists(3, $matches)) { $description = $matches[3]; // Starting line / Number of lines / Description if (preg_match('/^([1-9]\d*)(?:\s+((?1))\s*)?(.*)$/sux', $matches[3], $contentMatches)) { $startingLine = (int)$contentMatches[1]; if (isset($contentMatches[2]) && $contentMatches[2] !== '') { $lineCount = (int)$contentMatches[2]; } if (array_key_exists(3, $contentMatches)) { $description = $contentMatches[3]; } } } return new static( $filePath !== null?$filePath:$fileUri, $fileUri !== null, $startingLine, $lineCount, $description ); } /** * Returns the file path. * * @return string Path to a file to use as an example. * May also be an absolute URI. */ public function getFilePath() { return $this->filePath; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return $this->filePath . ($this->description ? ' ' . $this->description : ''); } /** * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). * * @param string $uri * * @return bool */ private function isUriRelative($uri) { return false === strpos($uri, ':'); } /** * @return int */ public function getStartingLine() { return $this->startingLine; } /** * @return int */ public function getLineCount() { return $this->lineCount; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}property-write tag in a Docblock. */ class PropertyWrite extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'property-write'; /** @var Type */ private $type; /** @var string */ protected $variableName = ''; /** * @param string $variableName * @param Type $type * @param Description $description */ public function __construct($variableName, Type $type = null, Description $description = null) { Assert::string($variableName); $this->variableName = $variableName; $this->type = $type; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::stringNotEmpty($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); $type = null; $variableName = ''; // if the first item that is encountered is not a variable; it is a type if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { $type = $typeResolver->resolve(array_shift($parts), $context); array_shift($parts); } // if the next item starts with a $ or ...$ it must be the variable name if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { $variableName = array_shift($parts); array_shift($parts); if (substr($variableName, 0, 1) === '$') { $variableName = substr($variableName, 1); } } $description = $descriptionFactory->create(implode('', $parts), $context); return new static($variableName, $type, $description); } /** * Returns the variable's name. * * @return string */ public function getVariableName() { return $this->variableName; } /** * Returns the variable's type or null if unknown. * * @return Type|null */ public function getType() { return $this->type; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return ($this->type ? $this->type . ' ' : '') . '$' . $this->variableName . ($this->description ? ' ' . $this->description : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}deprecated tag in a Docblock. */ final class Deprecated extends BaseTag implements Factory\StaticMethod { protected $name = 'deprecated'; /** * PCRE regular expression matching a version vector. * Assumes the "x" modifier. */ const REGEX_VECTOR = '(?: # Normal release vectors. \d\S* | # VCS version vectors. Per PHPCS, they are expected to # follow the form of the VCS name, followed by ":", followed # by the version vector itself. # By convention, popular VCSes like CVS, SVN and GIT use "$" # around the actual version vector. [^\s\:]+\:\s*\$[^\$]+\$ )'; /** @var string The version vector. */ private $version = ''; public function __construct($version = null, Description $description = null) { Assert::nullOrStringNotEmpty($version); $this->version = $version; $this->description = $description; } /** * @return static */ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) { Assert::nullOrString($body); if (empty($body)) { return new static(); } $matches = []; if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { return new static( null, null !== $descriptionFactory ? $descriptionFactory->create($body, $context) : null ); } return new static( $matches[1], $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) ); } /** * Gets the version section of the tag. * * @return string */ public function getVersion() { return $this->version; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return $this->version . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for the {@}param tag in a Docblock. */ final class Param extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'param'; /** @var Type */ private $type; /** @var string */ private $variableName = ''; /** @var bool determines whether this is a variadic argument */ private $isVariadic = false; /** * @param string $variableName * @param Type $type * @param bool $isVariadic * @param Description $description */ public function __construct($variableName, Type $type = null, $isVariadic = false, Description $description = null) { Assert::string($variableName); Assert::boolean($isVariadic); $this->variableName = $variableName; $this->type = $type; $this->isVariadic = $isVariadic; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::stringNotEmpty($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); $type = null; $variableName = ''; $isVariadic = false; // if the first item that is encountered is not a variable; it is a type if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { $type = $typeResolver->resolve(array_shift($parts), $context); array_shift($parts); } // if the next item starts with a $ or ...$ it must be the variable name if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$' || substr($parts[0], 0, 4) === '...$')) { $variableName = array_shift($parts); array_shift($parts); if (substr($variableName, 0, 3) === '...') { $isVariadic = true; $variableName = substr($variableName, 3); } if (substr($variableName, 0, 1) === '$') { $variableName = substr($variableName, 1); } } $description = $descriptionFactory->create(implode('', $parts), $context); return new static($variableName, $type, $isVariadic, $description); } /** * Returns the variable's name. * * @return string */ public function getVariableName() { return $this->variableName; } /** * Returns the variable's type or null if unknown. * * @return Type|null */ public function getType() { return $this->type; } /** * Returns whether this tag is variadic. * * @return boolean */ public function isVariadic() { return $this->isVariadic; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return ($this->type ? $this->type . ' ' : '') . ($this->isVariadic() ? '...' : '') . '$' . $this->variableName . ($this->description ? ' ' . $this->description : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use phpDocumentor\Reflection\Types\Void_; use Webmozart\Assert\Assert; /** * Reflection class for an {@}method in a Docblock. */ final class Method extends BaseTag implements Factory\StaticMethod { protected $name = 'method'; /** @var string */ private $methodName = ''; /** @var string[] */ private $arguments = []; /** @var bool */ private $isStatic = false; /** @var Type */ private $returnType; public function __construct( $methodName, array $arguments = [], Type $returnType = null, $static = false, Description $description = null ) { Assert::stringNotEmpty($methodName); Assert::boolean($static); if ($returnType === null) { $returnType = new Void_(); } $this->methodName = $methodName; $this->arguments = $this->filterArguments($arguments); $this->returnType = $returnType; $this->isStatic = $static; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::stringNotEmpty($body); Assert::allNotNull([ $typeResolver, $descriptionFactory ]); // 1. none or more whitespace // 2. optionally the keyword "static" followed by whitespace // 3. optionally a word with underscores followed by whitespace : as // type for the return value // 4. then optionally a word with underscores followed by () and // whitespace : as method name as used by phpDocumentor // 5. then a word with underscores, followed by ( and any character // until a ) and whitespace : as method name with signature // 6. any remaining text : as description if (!preg_match( '/^ # Static keyword # Declares a static method ONLY if type is also present (?: (static) \s+ )? # Return type (?: ( (?:[\w\|_\\\\]*\$this[\w\|_\\\\]*) | (?: (?:[\w\|_\\\\]+) # array notation (?:\[\])* )* ) \s+ )? # Legacy method name (not captured) (?: [\w_]+\(\)\s+ )? # Method name ([\w\|_\\\\]+) # Arguments (?: \(([^\)]*)\) )? \s* # Description (.*) $/sux', $body, $matches )) { return null; } list(, $static, $returnType, $methodName, $arguments, $description) = $matches; $static = $static === 'static'; if ($returnType === '') { $returnType = 'void'; } $returnType = $typeResolver->resolve($returnType, $context); $description = $descriptionFactory->create($description, $context); if (is_string($arguments) && strlen($arguments) > 0) { $arguments = explode(',', $arguments); foreach ($arguments as &$argument) { $argument = explode(' ', self::stripRestArg(trim($argument)), 2); if ($argument[0][0] === '$') { $argumentName = substr($argument[0], 1); $argumentType = new Void_(); } else { $argumentType = $typeResolver->resolve($argument[0], $context); $argumentName = ''; if (isset($argument[1])) { $argument[1] = self::stripRestArg($argument[1]); $argumentName = substr($argument[1], 1); } } $argument = [ 'name' => $argumentName, 'type' => $argumentType]; } } else { $arguments = []; } return new static($methodName, $arguments, $returnType, $static, $description); } /** * Retrieves the method name. * * @return string */ public function getMethodName() { return $this->methodName; } /** * @return string[] */ public function getArguments() { return $this->arguments; } /** * Checks whether the method tag describes a static method or not. * * @return bool TRUE if the method declaration is for a static method, FALSE otherwise. */ public function isStatic() { return $this->isStatic; } /** * @return Type */ public function getReturnType() { return $this->returnType; } public function __toString() { $arguments = []; foreach ($this->arguments as $argument) { $arguments[] = $argument['type'] . ' $' . $argument['name']; } return trim(($this->isStatic() ? 'static ' : '') . (string)$this->returnType . ' ' . $this->methodName . '(' . implode(', ', $arguments) . ')' . ($this->description ? ' ' . $this->description->render() : '')); } private function filterArguments($arguments) { foreach ($arguments as &$argument) { if (is_string($argument)) { $argument = [ 'name' => $argument ]; } if (! isset($argument['type'])) { $argument['type'] = new Void_(); } $keys = array_keys($argument); sort($keys); if ($keys !== [ 'name', 'type' ]) { throw new \InvalidArgumentException( 'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true) ); } } return $arguments; } private static function stripRestArg($argument) { if (strpos($argument, '...') === 0) { $argument = trim(substr($argument, 3)); } return $argument; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}throws tag in a Docblock. */ final class Throws extends BaseTag implements Factory\StaticMethod { protected $name = 'throws'; /** @var Type */ private $type; public function __construct(Type $type, Description $description = null) { $this->type = $type; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, TypeResolver $typeResolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::string($body); Assert::allNotNull([$typeResolver, $descriptionFactory]); $parts = preg_split('/\s+/Su', $body, 2); $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context); $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context); return new static($type, $description); } /** * Returns the type section of the variable. * * @return Type */ public function getType() { return $this->type; } public function __toString() { return $this->type . ' ' . $this->description; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}since tag in a Docblock. */ final class Since extends BaseTag implements Factory\StaticMethod { protected $name = 'since'; /** * PCRE regular expression matching a version vector. * Assumes the "x" modifier. */ const REGEX_VECTOR = '(?: # Normal release vectors. \d\S* | # VCS version vectors. Per PHPCS, they are expected to # follow the form of the VCS name, followed by ":", followed # by the version vector itself. # By convention, popular VCSes like CVS, SVN and GIT use "$" # around the actual version vector. [^\s\:]+\:\s*\$[^\$]+\$ )'; /** @var string The version vector. */ private $version = ''; public function __construct($version = null, Description $description = null) { Assert::nullOrStringNotEmpty($version); $this->version = $version; $this->description = $description; } /** * @return static */ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) { Assert::nullOrString($body); if (empty($body)) { return new static(); } $matches = []; if (! preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { return null; } return new static( $matches[1], $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) ); } /** * Gets the version section of the tag. * * @return string */ public function getVersion() { return $this->version; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return $this->version . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Parses a tag definition for a DocBlock. */ class Generic extends BaseTag implements Factory\StaticMethod { /** * Parses a tag and populates the member variables. * * @param string $name Name of the tag. * @param Description $description The contents of the given tag. */ public function __construct($name, Description $description = null) { $this->validateTagName($name); $this->name = $name; $this->description = $description; } /** * Creates a new tag that represents any unknown tag type. * * @param string $body * @param string $name * @param DescriptionFactory $descriptionFactory * @param TypeContext $context * * @return static */ public static function create( $body, $name = '', DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::string($body); Assert::stringNotEmpty($name); Assert::notNull($descriptionFactory); $description = $descriptionFactory && $body !== "" ? $descriptionFactory->create($body, $context) : null; return new static($name, $description); } /** * Returns the tag as a serialized string * * @return string */ public function __toString() { return ($this->description ? $this->description->render() : ''); } /** * Validates if the tag name matches the expected format, otherwise throws an exception. * * @param string $name * * @return void */ private function validateTagName($name) { if (! preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) { throw new \InvalidArgumentException( 'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' . 'hyphens and backslashes.' ); } } } * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a @link tag in a Docblock. */ final class Link extends BaseTag implements Factory\StaticMethod { protected $name = 'link'; /** @var string */ private $link = ''; /** * Initializes a link to a URL. * * @param string $link * @param Description $description */ public function __construct($link, Description $description = null) { Assert::string($link); $this->link = $link; $this->description = $description; } /** * {@inheritdoc} */ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) { Assert::string($body); Assert::notNull($descriptionFactory); $parts = preg_split('/\s+/Su', $body, 2); $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; return new static($parts[0], $description); } /** * Gets the link * * @return string */ public function getLink() { return $this->link; } /** * Returns a string representation for this tag. * * @return string */ public function __toString() { return $this->link . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a {@}uses tag in a Docblock. */ final class Uses extends BaseTag implements Factory\StaticMethod { protected $name = 'uses'; /** @var Fqsen */ protected $refers = null; /** * Initializes this tag. * * @param Fqsen $refers * @param Description $description */ public function __construct(Fqsen $refers, Description $description = null) { $this->refers = $refers; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, FqsenResolver $resolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::string($body); Assert::allNotNull([$resolver, $descriptionFactory]); $parts = preg_split('/\s+/Su', $body, 2); return new static( $resolver->resolve($parts[0], $context), $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context) ); } /** * Returns the structural element this tag refers to. * * @return Fqsen */ public function getReference() { return $this->refers; } /** * Returns a string representation of this tag. * * @return string */ public function __toString() { return $this->refers . ' ' . $this->description->render(); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; interface StaticMethod { public static function create($body); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; interface Strategy { public function create($body); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; interface Formatter { /** * Formats a tag into a string representation according to a specific format, such as Markdown. * * @param Tag $tag * * @return string */ public function format(Tag $tag); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Description; /** * Parses a tag definition for a DocBlock. */ abstract class BaseTag implements DocBlock\Tag { /** @var string Name of the tag */ protected $name = ''; /** @var Description|null Description of the tag. */ protected $description; /** * Gets the name of this tag. * * @return string The name of this tag. */ public function getName() { return $this->name; } public function getDescription() { return $this->description; } public function render(Formatter $formatter = null) { if ($formatter === null) { $formatter = new Formatter\PassthroughFormatter(); } return $formatter->format($this); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef; use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference; use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url; use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for an {@}see tag in a Docblock. */ class See extends BaseTag implements Factory\StaticMethod { protected $name = 'see'; /** @var Reference */ protected $refers = null; /** * Initializes this tag. * * @param Reference $refers * @param Description $description */ public function __construct(Reference $refers, Description $description = null) { $this->refers = $refers; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, FqsenResolver $resolver = null, DescriptionFactory $descriptionFactory = null, TypeContext $context = null ) { Assert::string($body); Assert::allNotNull([$resolver, $descriptionFactory]); $parts = preg_split('/\s+/Su', $body, 2); $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; // https://tools.ietf.org/html/rfc2396#section-3 if (preg_match('/\w:\/\/\w/i', $parts[0])) { return new static(new Url($parts[0]), $description); } return new static(new FqsenRef($resolver->resolve($parts[0], $context)), $description); } /** * Returns the ref of this tag. * * @return Reference */ public function getReference() { return $this->refers; } /** * Returns a string representation of this tag. * * @return string */ public function __toString() { return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Reflection class for a @covers tag in a Docblock. */ final class Covers extends BaseTag implements Factory\StaticMethod { protected $name = 'covers'; /** @var Fqsen */ private $refers = null; /** * Initializes this tag. * * @param Fqsen $refers * @param Description $description */ public function __construct(Fqsen $refers, Description $description = null) { $this->refers = $refers; $this->description = $description; } /** * {@inheritdoc} */ public static function create( $body, DescriptionFactory $descriptionFactory = null, FqsenResolver $resolver = null, TypeContext $context = null ) { Assert::string($body); Assert::notEmpty($body); $parts = preg_split('/\s+/Su', $body, 2); return new static( $resolver->resolve($parts[0], $context), $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context) ); } /** * Returns the structural element this tag refers to. * * @return Fqsen */ public function getReference() { return $this->refers; } /** * Returns a string representation of this tag. * * @return string */ public function __toString() { return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\Types\Context as TypeContext; /** * Creates a new Description object given a body of text. * * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the * Description object's `render` method. * * In addition to the above does a Description support two types of escape sequences: * * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}` * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description * of an inline tag. * * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping * over unexpected spaces as can be observed with tag descriptions. */ class DescriptionFactory { /** @var TagFactory */ private $tagFactory; /** * Initializes this factory with the means to construct (inline) tags. * * @param TagFactory $tagFactory */ public function __construct(TagFactory $tagFactory) { $this->tagFactory = $tagFactory; } /** * Returns the parsed text of this description. * * @param string $contents * @param TypeContext $context * * @return Description */ public function create($contents, TypeContext $context = null) { list($text, $tags) = $this->parse($this->lex($contents), $context); return new Description($text, $tags); } /** * Strips the contents from superfluous whitespace and splits the description into a series of tokens. * * @param string $contents * * @return string[] A series of tokens of which the description text is composed. */ private function lex($contents) { $contents = $this->removeSuperfluousStartingWhitespace($contents); // performance optimalization; if there is no inline tag, don't bother splitting it up. if (strpos($contents, '{@') === false) { return [$contents]; } return preg_split( '/\{ # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. (?!@\}) # We want to capture the whole tag line, but without the inline tag delimiters. (\@ # Match everything up to the next delimiter. [^{}]* # Nested inline tag content should not be captured, or it will appear in the result separately. (?: # Match nested inline tags. (?: # Because we did not catch the tag delimiters earlier, we must be explicit with them here. # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. \{(?1)?\} | # Make sure we match hanging "{". \{ ) # Match content after the nested inline tag. [^{}]* )* # If there are more inline tags, match them as well. We use "*" since there may not be any # nested inline tags. ) \}/Sux', $contents, null, PREG_SPLIT_DELIM_CAPTURE ); } /** * Parses the stream of tokens in to a new set of tokens containing Tags. * * @param string[] $tokens * @param TypeContext $context * * @return string[]|Tag[] */ private function parse($tokens, TypeContext $context) { $count = count($tokens); $tagCount = 0; $tags = []; for ($i = 1; $i < $count; $i += 2) { $tags[] = $this->tagFactory->create($tokens[$i], $context); $tokens[$i] = '%' . ++$tagCount . '$s'; } //In order to allow "literal" inline tags, the otherwise invalid //sequence "{@}" is changed to "@", and "{}" is changed to "}". //"%" is escaped to "%%" because of vsprintf. //See unit tests for examples. for ($i = 0; $i < $count; $i += 2) { $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]); } return [implode('', $tokens), $tags]; } /** * Removes the superfluous from a multi-line description. * * When a description has more than one line then it can happen that the second and subsequent lines have an * additional indentation. This is commonly in use with tags like this: * * {@}since 1.1.0 This is an example * description where we have an * indentation in the second and * subsequent lines. * * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent * lines and this may cause rendering issues when, for example, using a Markdown converter. * * @param string $contents * * @return string */ private function removeSuperfluousStartingWhitespace($contents) { $lines = explode("\n", $contents); // if there is only one line then we don't have lines with superfluous whitespace and // can use the contents as-is if (count($lines) <= 1) { return $contents; } // determine how many whitespace characters need to be stripped $startingSpaceCount = 9999999; for ($i = 1; $i < count($lines); $i++) { // lines with a no length do not count as they are not indented at all if (strlen(trim($lines[$i])) === 0) { continue; } // determine the number of prefixing spaces by checking the difference in line length before and after // an ltrim $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i]))); } // strip the number of spaces from each line if ($startingSpaceCount > 0) { for ($i = 1; $i < count($lines); $i++) { $lines[$i] = substr($lines[$i], $startingSpaceCount); } } return implode("\n", $lines); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Tags\Example; /** * Class used to find an example file's location based on a given ExampleDescriptor. */ class ExampleFinder { /** @var string */ private $sourceDirectory = ''; /** @var string[] */ private $exampleDirectories = []; /** * Attempts to find the example contents for the given descriptor. * * @param Example $example * * @return string */ public function find(Example $example) { $filename = $example->getFilePath(); $file = $this->getExampleFileContents($filename); if (!$file) { return "** File not found : {$filename} **"; } return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); } /** * Registers the project's root directory where an 'examples' folder can be expected. * * @param string $directory * * @return void */ public function setSourceDirectory($directory = '') { $this->sourceDirectory = $directory; } /** * Returns the project's root directory where an 'examples' folder can be expected. * * @return string */ public function getSourceDirectory() { return $this->sourceDirectory; } /** * Registers a series of directories that may contain examples. * * @param string[] $directories */ public function setExampleDirectories(array $directories) { $this->exampleDirectories = $directories; } /** * Returns a series of directories that may contain examples. * * @return string[] */ public function getExampleDirectories() { return $this->exampleDirectories; } /** * Attempts to find the requested example file and returns its contents or null if no file was found. * * This method will try several methods in search of the given example file, the first one it encounters is * returned: * * 1. Iterates through all examples folders for the given filename * 2. Checks the source folder for the given filename * 3. Checks the 'examples' folder in the current working directory for examples * 4. Checks the path relative to the current working directory for the given filename * * @param string $filename * * @return string|null */ private function getExampleFileContents($filename) { $normalizedPath = null; foreach ($this->exampleDirectories as $directory) { $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); if (is_readable($exampleFileFromConfig)) { $normalizedPath = $exampleFileFromConfig; break; } } if (!$normalizedPath) { if (is_readable($this->getExamplePathFromSource($filename))) { $normalizedPath = $this->getExamplePathFromSource($filename); } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) { $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); } elseif (is_readable($filename)) { $normalizedPath = $filename; } } return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null; } /** * Get example filepath based on the example directory inside your project. * * @param string $file * * @return string */ private function getExamplePathFromExampleDirectory($file) { return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file; } /** * Returns a path to the example file in the given directory.. * * @param string $directory * @param string $file * * @return string */ private function constructExamplePath($directory, $file) { return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file; } /** * Get example filepath based on sourcecode. * * @param string $file * * @return string */ private function getExamplePathFromSource($file) { return sprintf( '%s%s%s', trim($this->getSourceDirectory(), '\\/'), DIRECTORY_SEPARATOR, trim($file, '"') ); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Tags\Formatter; interface Tag { public function getName(); public static function create($body); public function render(Formatter $formatter = null); public function __toString(); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod; use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use Webmozart\Assert\Assert; /** * Creates a Tag object given the contents of a tag. * * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create` * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can * pass the dependencies that you need to construct a tag object. * * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to * > verify that a dependency is actually passed. * * This Factory also features a Service Locator component that is used to pass the right dependencies to the * `create` method of a tag; each dependency should be registered as a service or as a parameter. * * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface. */ final class StandardTagFactory implements TagFactory { /** PCRE regular expression matching a tag name. */ const REGEX_TAGNAME = '[\w\-\_\\\\]+'; /** * @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value. */ private $tagHandlerMappings = [ 'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author', 'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers', 'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated', // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', 'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link', 'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method', 'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param', 'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead', 'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property', 'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite', 'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return_', 'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See', 'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since', 'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source', 'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', 'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', 'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses', 'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_', 'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version' ]; /** * @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used. */ private $tagHandlerParameterCache = []; /** * @var FqsenResolver */ private $fqsenResolver; /** * @var mixed[] an array representing a simple Service Locator where we can store parameters and * services that can be inserted into the Factory Methods of Tag Handlers. */ private $serviceLocator = []; /** * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers. * * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property * is used. * * @param FqsenResolver $fqsenResolver * @param string[] $tagHandlers * * @see self::registerTagHandler() to add a new tag handler to the existing default list. */ public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null) { $this->fqsenResolver = $fqsenResolver; if ($tagHandlers !== null) { $this->tagHandlerMappings = $tagHandlers; } $this->addService($fqsenResolver, FqsenResolver::class); } /** * {@inheritDoc} */ public function create($tagLine, TypeContext $context = null) { if (! $context) { $context = new TypeContext(''); } list($tagName, $tagBody) = $this->extractTagParts($tagLine); if ($tagBody !== '' && $tagBody[0] === '[') { throw new \InvalidArgumentException( 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' ); } return $this->createTag($tagBody, $tagName, $context); } /** * {@inheritDoc} */ public function addParameter($name, $value) { $this->serviceLocator[$name] = $value; } /** * {@inheritDoc} */ public function addService($service, $alias = null) { $this->serviceLocator[$alias ?: get_class($service)] = $service; } /** * {@inheritDoc} */ public function registerTagHandler($tagName, $handler) { Assert::stringNotEmpty($tagName); Assert::stringNotEmpty($handler); Assert::classExists($handler); Assert::implementsInterface($handler, StaticMethod::class); if (strpos($tagName, '\\') && $tagName[0] !== '\\') { throw new \InvalidArgumentException( 'A namespaced tag must have a leading backslash as it must be fully qualified' ); } $this->tagHandlerMappings[$tagName] = $handler; } /** * Extracts all components for a tag. * * @param string $tagLine * * @return string[] */ private function extractTagParts($tagLine) { $matches = []; if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)/us', $tagLine, $matches)) { throw new \InvalidArgumentException( 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' ); } if (count($matches) < 3) { $matches[] = ''; } return array_slice($matches, 1); } /** * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the * body was invalid. * * @param string $body * @param string $name * @param TypeContext $context * * @return Tag|null */ private function createTag($body, $name, TypeContext $context) { $handlerClassName = $this->findHandlerClassName($name, $context); $arguments = $this->getArgumentsForParametersFromWiring( $this->fetchParametersForHandlerFactoryMethod($handlerClassName), $this->getServiceLocatorWithDynamicParameters($context, $name, $body) ); return call_user_func_array([$handlerClassName, 'create'], $arguments); } /** * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`). * * @param string $tagName * @param TypeContext $context * * @return string */ private function findHandlerClassName($tagName, TypeContext $context) { $handlerClassName = Generic::class; if (isset($this->tagHandlerMappings[$tagName])) { $handlerClassName = $this->tagHandlerMappings[$tagName]; } elseif ($this->isAnnotation($tagName)) { // TODO: Annotation support is planned for a later stage and as such is disabled for now // $tagName = (string)$this->fqsenResolver->resolve($tagName, $context); // if (isset($this->annotationMappings[$tagName])) { // $handlerClassName = $this->annotationMappings[$tagName]; // } } return $handlerClassName; } /** * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters. * * @param \ReflectionParameter[] $parameters * @param mixed[] $locator * * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters * is provided with this method. */ private function getArgumentsForParametersFromWiring($parameters, $locator) { $arguments = []; foreach ($parameters as $index => $parameter) { $typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null; if (isset($locator[$typeHint])) { $arguments[] = $locator[$typeHint]; continue; } $parameterName = $parameter->getName(); if (isset($locator[$parameterName])) { $arguments[] = $locator[$parameterName]; continue; } $arguments[] = null; } return $arguments; } /** * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given * tag handler class name. * * @param string $handlerClassName * * @return \ReflectionParameter[] */ private function fetchParametersForHandlerFactoryMethod($handlerClassName) { if (! isset($this->tagHandlerParameterCache[$handlerClassName])) { $methodReflection = new \ReflectionMethod($handlerClassName, 'create'); $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters(); } return $this->tagHandlerParameterCache[$handlerClassName]; } /** * Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and * Context. * * @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs. * @param string $tagName The name of the tag that may be passed onto the factory method of the Tag class. * @param string $tagBody The body of the tag that may be passed onto the factory method of the Tag class. * * @return mixed[] */ private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody) { $locator = array_merge( $this->serviceLocator, [ 'name' => $tagName, 'body' => $tagBody, TypeContext::class => $context ] ); return $locator; } /** * Returns whether the given tag belongs to an annotation. * * @param string $tagContent * * @todo this method should be populated once we implement Annotation notation support. * * @return bool */ private function isAnnotation($tagContent) { // 1. Contains a namespace separator // 2. Contains parenthesis // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part // of the annotation class name matches the found tag name return false; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\Types\Context as TypeContext; interface TagFactory { /** * Adds a parameter to the service locator that can be injected in a tag's factory method. * * When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to * typehint a parameter in the signature so that we can use that interface or class name to inject a dependency * (see {@see addService()} for more information on that). * * Another way is to check the name of the argument against the names in the Service Locator. With this method * you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching * name. * * Be aware that there are two reserved names: * * - name, representing the name of the tag. * - body, representing the complete body of the tag. * * These parameters are injected at the last moment and will override any existing parameter with those names. * * @param string $name * @param mixed $value * * @return void */ public function addParameter($name, $value); /** * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided. * * When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter * has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint. * * Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the * interface is passed as alias then every time that interface is requested the provided service will be returned. * * @param object $service * @param string $alias * * @return void */ public function addService($service); /** * Factory method responsible for instantiating the correct sub type. * * @param string $tagLine The text for this tag, including description. * @param TypeContext $context * * @throws \InvalidArgumentException if an invalid tag line was presented. * * @return Tag A new tag object. */ public function create($tagLine, TypeContext $context = null); /** * Registers a handler for tags. * * If you want to use your own tags then you can use this method to instruct the TagFactory to register the name * of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement the {@see Tag} interface (and thus * the create method). * * @param string $tagName Name of tag to register a handler for. When registering a namespaced tag, the full * name, along with a prefixing slash MUST be provided. * @param string $handler FQCN of handler. * * @throws \InvalidArgumentException if the tag name is not a string * @throws \InvalidArgumentException if the tag name is namespaced (contains backslashes) but does not start with * a backslash * @throws \InvalidArgumentException if the handler is not a string * @throws \InvalidArgumentException if the handler is not an existing class * @throws \InvalidArgumentException if the handler does not implement the {@see Tag} interface * * @return void */ public function registerTagHandler($tagName, $handler); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock; use Webmozart\Assert\Assert; /** * Converts a DocBlock back from an object to a complete DocComment including Asterisks. */ class Serializer { /** @var string The string to indent the comment with. */ protected $indentString = ' '; /** @var int The number of times the indent string is repeated. */ protected $indent = 0; /** @var bool Whether to indent the first line with the given indent amount and string. */ protected $isFirstLineIndented = true; /** @var int|null The max length of a line. */ protected $lineLength = null; /** @var DocBlock\Tags\Formatter A custom tag formatter. */ protected $tagFormatter = null; /** * Create a Serializer instance. * * @param int $indent The number of times the indent string is repeated. * @param string $indentString The string to indent the comment with. * @param bool $indentFirstLine Whether to indent the first line. * @param int|null $lineLength The max length of a line or NULL to disable line wrapping. * @param DocBlock\Tags\Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter. */ public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null, $tagFormatter = null) { Assert::integer($indent); Assert::string($indentString); Assert::boolean($indentFirstLine); Assert::nullOrInteger($lineLength); Assert::nullOrIsInstanceOf($tagFormatter, 'phpDocumentor\Reflection\DocBlock\Tags\Formatter'); $this->indent = $indent; $this->indentString = $indentString; $this->isFirstLineIndented = $indentFirstLine; $this->lineLength = $lineLength; $this->tagFormatter = $tagFormatter ?: new DocBlock\Tags\Formatter\PassthroughFormatter(); } /** * Generate a DocBlock comment. * * @param DocBlock $docblock The DocBlock to serialize. * * @return string The serialized doc block. */ public function getDocComment(DocBlock $docblock) { $indent = str_repeat($this->indentString, $this->indent); $firstIndent = $this->isFirstLineIndented ? $indent : ''; // 3 === strlen(' * ') $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null; $text = $this->removeTrailingSpaces( $indent, $this->addAsterisksForEachLine( $indent, $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength) ) ); $comment = "{$firstIndent}/**\n"; if ($text) { $comment .= "{$indent} * {$text}\n"; $comment .= "{$indent} *\n"; } $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment); $comment .= $indent . ' */'; return $comment; } /** * @param $indent * @param $text * @return mixed */ private function removeTrailingSpaces($indent, $text) { return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text); } /** * @param $indent * @param $text * @return mixed */ private function addAsterisksForEachLine($indent, $text) { return str_replace("\n", "\n{$indent} * ", $text); } /** * @param DocBlock $docblock * @param $wrapLength * @return string */ private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength) { $text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription() : ''); if ($wrapLength !== null) { $text = wordwrap($text, $wrapLength); return $text; } return $text; } /** * @param DocBlock $docblock * @param $wrapLength * @param $indent * @param $comment * @return string */ private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment) { foreach ($docblock->getTags() as $tag) { $tagText = $this->tagFormatter->format($tag); if ($wrapLength !== null) { $tagText = wordwrap($tagText, $wrapLength); } $tagText = str_replace("\n", "\n{$indent} * ", $tagText); $comment .= "{$indent} * {$tagText}\n"; } return $comment; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\TagFactory; use Webmozart\Assert\Assert; final class DocBlockFactory implements DocBlockFactoryInterface { /** @var DocBlock\DescriptionFactory */ private $descriptionFactory; /** @var DocBlock\TagFactory */ private $tagFactory; /** * Initializes this factory with the required subcontractors. * * @param DescriptionFactory $descriptionFactory * @param TagFactory $tagFactory */ public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory) { $this->descriptionFactory = $descriptionFactory; $this->tagFactory = $tagFactory; } /** * Factory method for easy instantiation. * * @param string[] $additionalTags * * @return DocBlockFactory */ public static function createInstance(array $additionalTags = []) { $fqsenResolver = new FqsenResolver(); $tagFactory = new StandardTagFactory($fqsenResolver); $descriptionFactory = new DescriptionFactory($tagFactory); $tagFactory->addService($descriptionFactory); $tagFactory->addService(new TypeResolver($fqsenResolver)); $docBlockFactory = new self($descriptionFactory, $tagFactory); foreach ($additionalTags as $tagName => $tagHandler) { $docBlockFactory->registerTagHandler($tagName, $tagHandler); } return $docBlockFactory; } /** * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the * getDocComment method (such as a ReflectionClass object). * @param Types\Context $context * @param Location $location * * @return DocBlock */ public function create($docblock, Types\Context $context = null, Location $location = null) { if (is_object($docblock)) { if (!method_exists($docblock, 'getDocComment')) { $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; throw new \InvalidArgumentException($exceptionMessage); } $docblock = $docblock->getDocComment(); } Assert::stringNotEmpty($docblock); if ($context === null) { $context = new Types\Context(''); } $parts = $this->splitDocBlock($this->stripDocComment($docblock)); list($templateMarker, $summary, $description, $tags) = $parts; return new DocBlock( $summary, $description ? $this->descriptionFactory->create($description, $context) : null, array_filter($this->parseTagBlock($tags, $context), function ($tag) { return $tag instanceof Tag; }), $context, $location, $templateMarker === '#@+', $templateMarker === '#@-' ); } public function registerTagHandler($tagName, $handler) { $this->tagFactory->registerTagHandler($tagName, $handler); } /** * Strips the asterisks from the DocBlock comment. * * @param string $comment String containing the comment text. * * @return string */ private function stripDocComment($comment) { $comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment)); // reg ex above is not able to remove */ from a single line docblock if (substr($comment, -2) === '*/') { $comment = trim(substr($comment, 0, -2)); } return str_replace(["\r\n", "\r"], "\n", $comment); } /** * Splits the DocBlock into a template marker, summary, description and block of tags. * * @param string $comment Comment to split into the sub-parts. * * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. * @author Mike van Riel for extending the regex with template marker support. * * @return string[] containing the template marker (if any), summary, description and a string containing the tags. */ private function splitDocBlock($comment) { // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the // performance impact of running a regular expression if (strpos($comment, '@') === 0) { return ['', '', '', $comment]; } // clears all extra horizontal whitespace from the line endings to prevent parsing issues $comment = preg_replace('/\h*$/Sum', '', $comment); /* * Splits the docblock into a template marker, summary, description and tags section. * * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may * occur after it and will be stripped). * - The short description is started from the first character until a dot is encountered followed by a * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing * errors). This is optional. * - The long description, any character until a new line is encountered followed by an @ and word * characters (a tag). This is optional. * - Tags; the remaining characters * * Big thanks to RichardJ for contributing this Regular Expression */ preg_match( '/ \A # 1. Extract the template marker (?:(\#\@\+|\#\@\-)\n?)? # 2. Extract the summary (?: (?! @\pL ) # The summary may not start with an @ ( [^\n.]+ (?: (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line [^\n.]+ # Include anything else )* \.? )? ) # 3. Extract the description (?: \s* # Some form of whitespace _must_ precede a description because a summary must be there (?! @\pL ) # The description may not start with an @ ( [^\n]+ (?: \n+ (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line [^\n]+ # Include anything else )* ) )? # 4. Extract the tags (anything that follows) (\s+ [\s\S]*)? # everything that follows /ux', $comment, $matches ); array_shift($matches); while (count($matches) < 4) { $matches[] = ''; } return $matches; } /** * Creates the tag objects. * * @param string $tags Tag block to parse. * @param Types\Context $context Context of the parsed Tag * * @return DocBlock\Tag[] */ private function parseTagBlock($tags, Types\Context $context) { $tags = $this->filterTagBlock($tags); if (!$tags) { return []; } $result = $this->splitTagBlockIntoTagLines($tags); foreach ($result as $key => $tagLine) { $result[$key] = $this->tagFactory->create(trim($tagLine), $context); } return $result; } /** * @param string $tags * * @return string[] */ private function splitTagBlockIntoTagLines($tags) { $result = []; foreach (explode("\n", $tags) as $tag_line) { if (isset($tag_line[0]) && ($tag_line[0] === '@')) { $result[] = $tag_line; } else { $result[count($result) - 1] .= "\n" . $tag_line; } } return $result; } /** * @param $tags * @return string */ private function filterTagBlock($tags) { $tags = trim($tags); if (!$tags) { return null; } if ('@' !== $tags[0]) { // @codeCoverageIgnoreStart // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that // we didn't foresee. throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); // @codeCoverageIgnoreEnd } return $tags; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Timer; final class Timer { /** * @var int[] */ private static $sizes = [ 'GB' => 1073741824, 'MB' => 1048576, 'KB' => 1024, ]; /** * @var int[] */ private static $times = [ 'hour' => 3600000, 'minute' => 60000, 'second' => 1000, ]; /** * @var float[] */ private static $startTimes = []; public static function start(): void { self::$startTimes[] = \microtime(true); } public static function stop(): float { return \microtime(true) - \array_pop(self::$startTimes); } public static function bytesToString(int $bytes): string { foreach (self::$sizes as $unit => $value) { if ($bytes >= $value) { $size = \sprintf('%.2f', $bytes >= 1024 ? $bytes / $value : $bytes); return $size . ' ' . $unit; } } return $bytes . ' byte' . ($bytes !== 1 ? 's' : ''); } public static function secondsToTimeString(float $time): string { $ms = \round($time * 1000); foreach (self::$times as $unit => $value) { if ($ms >= $value) { $time = \floor($ms / $value * 100.0) / 100.0; return $time . ' ' . ($time == 1 ? $unit : $unit . 's'); } } return $ms . ' ms'; } /** * @throws RuntimeException */ public static function timeSinceStartOfRequest(): string { if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { $startOfRequest = $_SERVER['REQUEST_TIME_FLOAT']; } elseif (isset($_SERVER['REQUEST_TIME'])) { $startOfRequest = $_SERVER['REQUEST_TIME']; } else { throw new RuntimeException('Cannot determine time at which the request started'); } return self::secondsToTimeString(\microtime(true) - $startOfRequest); } /** * @throws RuntimeException */ public static function resourceUsage(): string { return \sprintf( 'Time: %s, Memory: %s', self::timeSinceStartOfRequest(), self::bytesToString(\memory_get_peak_usage(true)) ); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Timer; interface Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Timer; final class RuntimeException extends \RuntimeException implements Exception { } phpunit/php-timer Copyright (c) 2010-2019, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ResourceOperations; final class ResourceOperations { /** * @return string[] */ public static function getFunctions(): array { return [ 'Directory::close', 'Directory::read', 'Directory::rewind', 'DirectoryIterator::openFile', 'FilesystemIterator::openFile', 'Gmagick::readimagefile', 'HttpResponse::getRequestBodyStream', 'HttpResponse::getStream', 'HttpResponse::setStream', 'Imagick::pingImageFile', 'Imagick::readImageFile', 'Imagick::writeImageFile', 'Imagick::writeImagesFile', 'MongoGridFSCursor::__construct', 'MongoGridFSFile::getResource', 'MysqlndUhConnection::stmtInit', 'MysqlndUhConnection::storeResult', 'MysqlndUhConnection::useResult', 'PDF_activate_item', 'PDF_add_launchlink', 'PDF_add_locallink', 'PDF_add_nameddest', 'PDF_add_note', 'PDF_add_pdflink', 'PDF_add_table_cell', 'PDF_add_textflow', 'PDF_add_thumbnail', 'PDF_add_weblink', 'PDF_arc', 'PDF_arcn', 'PDF_attach_file', 'PDF_begin_document', 'PDF_begin_font', 'PDF_begin_glyph', 'PDF_begin_item', 'PDF_begin_layer', 'PDF_begin_page', 'PDF_begin_page_ext', 'PDF_begin_pattern', 'PDF_begin_template', 'PDF_begin_template_ext', 'PDF_circle', 'PDF_clip', 'PDF_close', 'PDF_close_image', 'PDF_close_pdi', 'PDF_close_pdi_page', 'PDF_closepath', 'PDF_closepath_fill_stroke', 'PDF_closepath_stroke', 'PDF_concat', 'PDF_continue_text', 'PDF_create_3dview', 'PDF_create_action', 'PDF_create_annotation', 'PDF_create_bookmark', 'PDF_create_field', 'PDF_create_fieldgroup', 'PDF_create_gstate', 'PDF_create_pvf', 'PDF_create_textflow', 'PDF_curveto', 'PDF_define_layer', 'PDF_delete', 'PDF_delete_pvf', 'PDF_delete_table', 'PDF_delete_textflow', 'PDF_encoding_set_char', 'PDF_end_document', 'PDF_end_font', 'PDF_end_glyph', 'PDF_end_item', 'PDF_end_layer', 'PDF_end_page', 'PDF_end_page_ext', 'PDF_end_pattern', 'PDF_end_template', 'PDF_endpath', 'PDF_fill', 'PDF_fill_imageblock', 'PDF_fill_pdfblock', 'PDF_fill_stroke', 'PDF_fill_textblock', 'PDF_findfont', 'PDF_fit_image', 'PDF_fit_pdi_page', 'PDF_fit_table', 'PDF_fit_textflow', 'PDF_fit_textline', 'PDF_get_apiname', 'PDF_get_buffer', 'PDF_get_errmsg', 'PDF_get_errnum', 'PDF_get_parameter', 'PDF_get_pdi_parameter', 'PDF_get_pdi_value', 'PDF_get_value', 'PDF_info_font', 'PDF_info_matchbox', 'PDF_info_table', 'PDF_info_textflow', 'PDF_info_textline', 'PDF_initgraphics', 'PDF_lineto', 'PDF_load_3ddata', 'PDF_load_font', 'PDF_load_iccprofile', 'PDF_load_image', 'PDF_makespotcolor', 'PDF_moveto', 'PDF_new', 'PDF_open_ccitt', 'PDF_open_file', 'PDF_open_image', 'PDF_open_image_file', 'PDF_open_memory_image', 'PDF_open_pdi', 'PDF_open_pdi_document', 'PDF_open_pdi_page', 'PDF_pcos_get_number', 'PDF_pcos_get_stream', 'PDF_pcos_get_string', 'PDF_place_image', 'PDF_place_pdi_page', 'PDF_process_pdi', 'PDF_rect', 'PDF_restore', 'PDF_resume_page', 'PDF_rotate', 'PDF_save', 'PDF_scale', 'PDF_set_border_color', 'PDF_set_border_dash', 'PDF_set_border_style', 'PDF_set_gstate', 'PDF_set_info', 'PDF_set_layer_dependency', 'PDF_set_parameter', 'PDF_set_text_pos', 'PDF_set_value', 'PDF_setcolor', 'PDF_setdash', 'PDF_setdashpattern', 'PDF_setflat', 'PDF_setfont', 'PDF_setgray', 'PDF_setgray_fill', 'PDF_setgray_stroke', 'PDF_setlinecap', 'PDF_setlinejoin', 'PDF_setlinewidth', 'PDF_setmatrix', 'PDF_setmiterlimit', 'PDF_setrgbcolor', 'PDF_setrgbcolor_fill', 'PDF_setrgbcolor_stroke', 'PDF_shading', 'PDF_shading_pattern', 'PDF_shfill', 'PDF_show', 'PDF_show_boxed', 'PDF_show_xy', 'PDF_skew', 'PDF_stringwidth', 'PDF_stroke', 'PDF_suspend_page', 'PDF_translate', 'PDF_utf16_to_utf8', 'PDF_utf32_to_utf16', 'PDF_utf8_to_utf16', 'PDO::pgsqlLOBOpen', 'RarEntry::getStream', 'SQLite3::openBlob', 'SWFMovie::saveToFile', 'SplFileInfo::openFile', 'SplFileObject::openFile', 'SplTempFileObject::openFile', 'V8Js::compileString', 'V8Js::executeScript', 'Vtiful\Kernel\Excel::setColumn', 'Vtiful\Kernel\Excel::setRow', 'Vtiful\Kernel\Format::align', 'Vtiful\Kernel\Format::bold', 'Vtiful\Kernel\Format::italic', 'Vtiful\Kernel\Format::underline', 'XMLWriter::openMemory', 'XMLWriter::openURI', 'ZipArchive::getStream', 'Zookeeper::setLogStream', 'apc_bin_dumpfile', 'apc_bin_loadfile', 'bbcode_add_element', 'bbcode_add_smiley', 'bbcode_create', 'bbcode_destroy', 'bbcode_parse', 'bbcode_set_arg_parser', 'bbcode_set_flags', 'bcompiler_read', 'bcompiler_write_class', 'bcompiler_write_constant', 'bcompiler_write_exe_footer', 'bcompiler_write_file', 'bcompiler_write_footer', 'bcompiler_write_function', 'bcompiler_write_functions_from_file', 'bcompiler_write_header', 'bcompiler_write_included_filename', 'bzclose', 'bzerrno', 'bzerror', 'bzerrstr', 'bzflush', 'bzopen', 'bzread', 'bzwrite', 'cairo_surface_write_to_png', 'closedir', 'copy', 'crack_closedict', 'crack_opendict', 'cubrid_bind', 'cubrid_close_prepare', 'cubrid_close_request', 'cubrid_col_get', 'cubrid_col_size', 'cubrid_column_names', 'cubrid_column_types', 'cubrid_commit', 'cubrid_connect', 'cubrid_connect_with_url', 'cubrid_current_oid', 'cubrid_db_parameter', 'cubrid_disconnect', 'cubrid_drop', 'cubrid_fetch', 'cubrid_free_result', 'cubrid_get', 'cubrid_get_autocommit', 'cubrid_get_charset', 'cubrid_get_class_name', 'cubrid_get_db_parameter', 'cubrid_get_query_timeout', 'cubrid_get_server_info', 'cubrid_insert_id', 'cubrid_is_instance', 'cubrid_lob2_bind', 'cubrid_lob2_close', 'cubrid_lob2_export', 'cubrid_lob2_import', 'cubrid_lob2_new', 'cubrid_lob2_read', 'cubrid_lob2_seek', 'cubrid_lob2_seek64', 'cubrid_lob2_size', 'cubrid_lob2_size64', 'cubrid_lob2_tell', 'cubrid_lob2_tell64', 'cubrid_lob2_write', 'cubrid_lob_export', 'cubrid_lob_get', 'cubrid_lob_send', 'cubrid_lob_size', 'cubrid_lock_read', 'cubrid_lock_write', 'cubrid_move_cursor', 'cubrid_next_result', 'cubrid_num_cols', 'cubrid_num_rows', 'cubrid_pconnect', 'cubrid_pconnect_with_url', 'cubrid_prepare', 'cubrid_put', 'cubrid_query', 'cubrid_rollback', 'cubrid_schema', 'cubrid_seq_add', 'cubrid_seq_drop', 'cubrid_seq_insert', 'cubrid_seq_put', 'cubrid_set_add', 'cubrid_set_autocommit', 'cubrid_set_db_parameter', 'cubrid_set_drop', 'cubrid_set_query_timeout', 'cubrid_unbuffered_query', 'curl_close', 'curl_copy_handle', 'curl_errno', 'curl_error', 'curl_escape', 'curl_exec', 'curl_getinfo', 'curl_multi_add_handle', 'curl_multi_close', 'curl_multi_errno', 'curl_multi_exec', 'curl_multi_getcontent', 'curl_multi_info_read', 'curl_multi_remove_handle', 'curl_multi_select', 'curl_multi_setopt', 'curl_pause', 'curl_reset', 'curl_setopt', 'curl_setopt_array', 'curl_share_close', 'curl_share_errno', 'curl_share_init', 'curl_share_setopt', 'curl_unescape', 'cyrus_authenticate', 'cyrus_bind', 'cyrus_close', 'cyrus_connect', 'cyrus_query', 'cyrus_unbind', 'db2_autocommit', 'db2_bind_param', 'db2_client_info', 'db2_close', 'db2_column_privileges', 'db2_columns', 'db2_commit', 'db2_conn_error', 'db2_conn_errormsg', 'db2_connect', 'db2_cursor_type', 'db2_exec', 'db2_execute', 'db2_fetch_array', 'db2_fetch_assoc', 'db2_fetch_both', 'db2_fetch_object', 'db2_fetch_row', 'db2_field_display_size', 'db2_field_name', 'db2_field_num', 'db2_field_precision', 'db2_field_scale', 'db2_field_type', 'db2_field_width', 'db2_foreign_keys', 'db2_free_result', 'db2_free_stmt', 'db2_get_option', 'db2_last_insert_id', 'db2_lob_read', 'db2_next_result', 'db2_num_fields', 'db2_num_rows', 'db2_pclose', 'db2_pconnect', 'db2_prepare', 'db2_primary_keys', 'db2_procedure_columns', 'db2_procedures', 'db2_result', 'db2_rollback', 'db2_server_info', 'db2_set_option', 'db2_special_columns', 'db2_statistics', 'db2_stmt_error', 'db2_stmt_errormsg', 'db2_table_privileges', 'db2_tables', 'dba_close', 'dba_delete', 'dba_exists', 'dba_fetch', 'dba_firstkey', 'dba_insert', 'dba_nextkey', 'dba_open', 'dba_optimize', 'dba_popen', 'dba_replace', 'dba_sync', 'dbplus_add', 'dbplus_aql', 'dbplus_close', 'dbplus_curr', 'dbplus_find', 'dbplus_first', 'dbplus_flush', 'dbplus_freelock', 'dbplus_freerlocks', 'dbplus_getlock', 'dbplus_getunique', 'dbplus_info', 'dbplus_last', 'dbplus_lockrel', 'dbplus_next', 'dbplus_open', 'dbplus_prev', 'dbplus_rchperm', 'dbplus_rcreate', 'dbplus_rcrtexact', 'dbplus_rcrtlike', 'dbplus_restorepos', 'dbplus_rkeys', 'dbplus_ropen', 'dbplus_rquery', 'dbplus_rrename', 'dbplus_rsecindex', 'dbplus_runlink', 'dbplus_rzap', 'dbplus_savepos', 'dbplus_setindex', 'dbplus_setindexbynumber', 'dbplus_sql', 'dbplus_tremove', 'dbplus_undo', 'dbplus_undoprepare', 'dbplus_unlockrel', 'dbplus_unselect', 'dbplus_update', 'dbplus_xlockrel', 'dbplus_xunlockrel', 'deflate_add', 'dio_close', 'dio_fcntl', 'dio_open', 'dio_read', 'dio_seek', 'dio_stat', 'dio_tcsetattr', 'dio_truncate', 'dio_write', 'dir', 'eio_busy', 'eio_cancel', 'eio_chmod', 'eio_chown', 'eio_close', 'eio_custom', 'eio_dup2', 'eio_fallocate', 'eio_fchmod', 'eio_fchown', 'eio_fdatasync', 'eio_fstat', 'eio_fstatvfs', 'eio_fsync', 'eio_ftruncate', 'eio_futime', 'eio_get_last_error', 'eio_grp', 'eio_grp_add', 'eio_grp_cancel', 'eio_grp_limit', 'eio_link', 'eio_lstat', 'eio_mkdir', 'eio_mknod', 'eio_nop', 'eio_open', 'eio_read', 'eio_readahead', 'eio_readdir', 'eio_readlink', 'eio_realpath', 'eio_rename', 'eio_rmdir', 'eio_seek', 'eio_sendfile', 'eio_stat', 'eio_statvfs', 'eio_symlink', 'eio_sync', 'eio_sync_file_range', 'eio_syncfs', 'eio_truncate', 'eio_unlink', 'eio_utime', 'eio_write', 'enchant_broker_describe', 'enchant_broker_dict_exists', 'enchant_broker_free', 'enchant_broker_free_dict', 'enchant_broker_get_dict_path', 'enchant_broker_get_error', 'enchant_broker_init', 'enchant_broker_list_dicts', 'enchant_broker_request_dict', 'enchant_broker_request_pwl_dict', 'enchant_broker_set_dict_path', 'enchant_broker_set_ordering', 'enchant_dict_add_to_personal', 'enchant_dict_add_to_session', 'enchant_dict_check', 'enchant_dict_describe', 'enchant_dict_get_error', 'enchant_dict_is_in_session', 'enchant_dict_quick_check', 'enchant_dict_store_replacement', 'enchant_dict_suggest', 'event_add', 'event_base_free', 'event_base_loop', 'event_base_loopbreak', 'event_base_loopexit', 'event_base_new', 'event_base_priority_init', 'event_base_reinit', 'event_base_set', 'event_buffer_base_set', 'event_buffer_disable', 'event_buffer_enable', 'event_buffer_fd_set', 'event_buffer_free', 'event_buffer_new', 'event_buffer_priority_set', 'event_buffer_read', 'event_buffer_set_callback', 'event_buffer_timeout_set', 'event_buffer_watermark_set', 'event_buffer_write', 'event_del', 'event_free', 'event_new', 'event_priority_set', 'event_set', 'event_timer_add', 'event_timer_del', 'event_timer_pending', 'event_timer_set', 'expect_expectl', 'expect_popen', 'fam_cancel_monitor', 'fam_close', 'fam_monitor_collection', 'fam_monitor_directory', 'fam_monitor_file', 'fam_next_event', 'fam_open', 'fam_pending', 'fam_resume_monitor', 'fam_suspend_monitor', 'fann_cascadetrain_on_data', 'fann_cascadetrain_on_file', 'fann_clear_scaling_params', 'fann_copy', 'fann_create_from_file', 'fann_create_shortcut_array', 'fann_create_standard', 'fann_create_standard_array', 'fann_create_train', 'fann_create_train_from_callback', 'fann_descale_input', 'fann_descale_output', 'fann_descale_train', 'fann_destroy', 'fann_destroy_train', 'fann_duplicate_train_data', 'fann_get_MSE', 'fann_get_activation_function', 'fann_get_activation_steepness', 'fann_get_bias_array', 'fann_get_bit_fail', 'fann_get_bit_fail_limit', 'fann_get_cascade_activation_functions', 'fann_get_cascade_activation_functions_count', 'fann_get_cascade_activation_steepnesses', 'fann_get_cascade_activation_steepnesses_count', 'fann_get_cascade_candidate_change_fraction', 'fann_get_cascade_candidate_limit', 'fann_get_cascade_candidate_stagnation_epochs', 'fann_get_cascade_max_cand_epochs', 'fann_get_cascade_max_out_epochs', 'fann_get_cascade_min_cand_epochs', 'fann_get_cascade_min_out_epochs', 'fann_get_cascade_num_candidate_groups', 'fann_get_cascade_num_candidates', 'fann_get_cascade_output_change_fraction', 'fann_get_cascade_output_stagnation_epochs', 'fann_get_cascade_weight_multiplier', 'fann_get_connection_array', 'fann_get_connection_rate', 'fann_get_errno', 'fann_get_errstr', 'fann_get_layer_array', 'fann_get_learning_momentum', 'fann_get_learning_rate', 'fann_get_network_type', 'fann_get_num_input', 'fann_get_num_layers', 'fann_get_num_output', 'fann_get_quickprop_decay', 'fann_get_quickprop_mu', 'fann_get_rprop_decrease_factor', 'fann_get_rprop_delta_max', 'fann_get_rprop_delta_min', 'fann_get_rprop_delta_zero', 'fann_get_rprop_increase_factor', 'fann_get_sarprop_step_error_shift', 'fann_get_sarprop_step_error_threshold_factor', 'fann_get_sarprop_temperature', 'fann_get_sarprop_weight_decay_shift', 'fann_get_total_connections', 'fann_get_total_neurons', 'fann_get_train_error_function', 'fann_get_train_stop_function', 'fann_get_training_algorithm', 'fann_init_weights', 'fann_length_train_data', 'fann_merge_train_data', 'fann_num_input_train_data', 'fann_num_output_train_data', 'fann_randomize_weights', 'fann_read_train_from_file', 'fann_reset_errno', 'fann_reset_errstr', 'fann_run', 'fann_save', 'fann_save_train', 'fann_scale_input', 'fann_scale_input_train_data', 'fann_scale_output', 'fann_scale_output_train_data', 'fann_scale_train', 'fann_scale_train_data', 'fann_set_activation_function', 'fann_set_activation_function_hidden', 'fann_set_activation_function_layer', 'fann_set_activation_function_output', 'fann_set_activation_steepness', 'fann_set_activation_steepness_hidden', 'fann_set_activation_steepness_layer', 'fann_set_activation_steepness_output', 'fann_set_bit_fail_limit', 'fann_set_callback', 'fann_set_cascade_activation_functions', 'fann_set_cascade_activation_steepnesses', 'fann_set_cascade_candidate_change_fraction', 'fann_set_cascade_candidate_limit', 'fann_set_cascade_candidate_stagnation_epochs', 'fann_set_cascade_max_cand_epochs', 'fann_set_cascade_max_out_epochs', 'fann_set_cascade_min_cand_epochs', 'fann_set_cascade_min_out_epochs', 'fann_set_cascade_num_candidate_groups', 'fann_set_cascade_output_change_fraction', 'fann_set_cascade_output_stagnation_epochs', 'fann_set_cascade_weight_multiplier', 'fann_set_error_log', 'fann_set_input_scaling_params', 'fann_set_learning_momentum', 'fann_set_learning_rate', 'fann_set_output_scaling_params', 'fann_set_quickprop_decay', 'fann_set_quickprop_mu', 'fann_set_rprop_decrease_factor', 'fann_set_rprop_delta_max', 'fann_set_rprop_delta_min', 'fann_set_rprop_delta_zero', 'fann_set_rprop_increase_factor', 'fann_set_sarprop_step_error_shift', 'fann_set_sarprop_step_error_threshold_factor', 'fann_set_sarprop_temperature', 'fann_set_sarprop_weight_decay_shift', 'fann_set_scaling_params', 'fann_set_train_error_function', 'fann_set_train_stop_function', 'fann_set_training_algorithm', 'fann_set_weight', 'fann_set_weight_array', 'fann_shuffle_train_data', 'fann_subset_train_data', 'fann_test', 'fann_test_data', 'fann_train', 'fann_train_epoch', 'fann_train_on_data', 'fann_train_on_file', 'fbsql_affected_rows', 'fbsql_autocommit', 'fbsql_blob_size', 'fbsql_change_user', 'fbsql_clob_size', 'fbsql_close', 'fbsql_commit', 'fbsql_connect', 'fbsql_create_blob', 'fbsql_create_clob', 'fbsql_create_db', 'fbsql_data_seek', 'fbsql_database', 'fbsql_database_password', 'fbsql_db_query', 'fbsql_db_status', 'fbsql_drop_db', 'fbsql_errno', 'fbsql_error', 'fbsql_fetch_array', 'fbsql_fetch_assoc', 'fbsql_fetch_field', 'fbsql_fetch_lengths', 'fbsql_fetch_object', 'fbsql_fetch_row', 'fbsql_field_flags', 'fbsql_field_len', 'fbsql_field_name', 'fbsql_field_seek', 'fbsql_field_table', 'fbsql_field_type', 'fbsql_free_result', 'fbsql_get_autostart_info', 'fbsql_hostname', 'fbsql_insert_id', 'fbsql_list_dbs', 'fbsql_list_fields', 'fbsql_list_tables', 'fbsql_next_result', 'fbsql_num_fields', 'fbsql_num_rows', 'fbsql_password', 'fbsql_pconnect', 'fbsql_query', 'fbsql_read_blob', 'fbsql_read_clob', 'fbsql_result', 'fbsql_rollback', 'fbsql_rows_fetched', 'fbsql_select_db', 'fbsql_set_characterset', 'fbsql_set_lob_mode', 'fbsql_set_password', 'fbsql_set_transaction', 'fbsql_start_db', 'fbsql_stop_db', 'fbsql_table_name', 'fbsql_username', 'fclose', 'fdf_add_doc_javascript', 'fdf_add_template', 'fdf_close', 'fdf_create', 'fdf_enum_values', 'fdf_get_ap', 'fdf_get_attachment', 'fdf_get_encoding', 'fdf_get_file', 'fdf_get_flags', 'fdf_get_opt', 'fdf_get_status', 'fdf_get_value', 'fdf_get_version', 'fdf_next_field_name', 'fdf_open', 'fdf_open_string', 'fdf_remove_item', 'fdf_save', 'fdf_save_string', 'fdf_set_ap', 'fdf_set_encoding', 'fdf_set_file', 'fdf_set_flags', 'fdf_set_javascript_action', 'fdf_set_on_import_javascript', 'fdf_set_opt', 'fdf_set_status', 'fdf_set_submit_form_action', 'fdf_set_target_frame', 'fdf_set_value', 'fdf_set_version', 'feof', 'fflush', 'ffmpeg_frame::__construct', 'ffmpeg_frame::toGDImage', 'fgetc', 'fgetcsv', 'fgets', 'fgetss', 'file', 'file_get_contents', 'file_put_contents', 'finfo::buffer', 'finfo::file', 'finfo_buffer', 'finfo_close', 'finfo_file', 'finfo_open', 'finfo_set_flags', 'flock', 'fopen', 'fpassthru', 'fprintf', 'fputcsv', 'fputs', 'fread', 'fscanf', 'fseek', 'fstat', 'ftell', 'ftp_alloc', 'ftp_append', 'ftp_cdup', 'ftp_chdir', 'ftp_chmod', 'ftp_close', 'ftp_delete', 'ftp_exec', 'ftp_fget', 'ftp_fput', 'ftp_get', 'ftp_get_option', 'ftp_login', 'ftp_mdtm', 'ftp_mkdir', 'ftp_mlsd', 'ftp_nb_continue', 'ftp_nb_fget', 'ftp_nb_fput', 'ftp_nb_get', 'ftp_nb_put', 'ftp_nlist', 'ftp_pasv', 'ftp_put', 'ftp_pwd', 'ftp_quit', 'ftp_raw', 'ftp_rawlist', 'ftp_rename', 'ftp_rmdir', 'ftp_set_option', 'ftp_site', 'ftp_size', 'ftp_systype', 'ftruncate', 'fwrite', 'get_resource_type', 'gmp_div', 'gnupg::init', 'gnupg_adddecryptkey', 'gnupg_addencryptkey', 'gnupg_addsignkey', 'gnupg_cleardecryptkeys', 'gnupg_clearencryptkeys', 'gnupg_clearsignkeys', 'gnupg_decrypt', 'gnupg_decryptverify', 'gnupg_encrypt', 'gnupg_encryptsign', 'gnupg_export', 'gnupg_geterror', 'gnupg_getprotocol', 'gnupg_import', 'gnupg_init', 'gnupg_keyinfo', 'gnupg_setarmor', 'gnupg_seterrormode', 'gnupg_setsignmode', 'gnupg_sign', 'gnupg_verify', 'gupnp_context_get_host_ip', 'gupnp_context_get_port', 'gupnp_context_get_subscription_timeout', 'gupnp_context_host_path', 'gupnp_context_new', 'gupnp_context_set_subscription_timeout', 'gupnp_context_timeout_add', 'gupnp_context_unhost_path', 'gupnp_control_point_browse_start', 'gupnp_control_point_browse_stop', 'gupnp_control_point_callback_set', 'gupnp_control_point_new', 'gupnp_device_action_callback_set', 'gupnp_device_info_get', 'gupnp_device_info_get_service', 'gupnp_root_device_get_available', 'gupnp_root_device_get_relative_location', 'gupnp_root_device_new', 'gupnp_root_device_set_available', 'gupnp_root_device_start', 'gupnp_root_device_stop', 'gupnp_service_action_get', 'gupnp_service_action_return', 'gupnp_service_action_return_error', 'gupnp_service_action_set', 'gupnp_service_freeze_notify', 'gupnp_service_info_get', 'gupnp_service_info_get_introspection', 'gupnp_service_introspection_get_state_variable', 'gupnp_service_notify', 'gupnp_service_proxy_action_get', 'gupnp_service_proxy_action_set', 'gupnp_service_proxy_add_notify', 'gupnp_service_proxy_callback_set', 'gupnp_service_proxy_get_subscribed', 'gupnp_service_proxy_remove_notify', 'gupnp_service_proxy_send_action', 'gupnp_service_proxy_set_subscribed', 'gupnp_service_thaw_notify', 'gzclose', 'gzeof', 'gzgetc', 'gzgets', 'gzgetss', 'gzpassthru', 'gzputs', 'gzread', 'gzrewind', 'gzseek', 'gztell', 'gzwrite', 'hash_update_stream', 'http\Env\Response::send', 'http_get_request_body_stream', 'ibase_add_user', 'ibase_affected_rows', 'ibase_backup', 'ibase_blob_add', 'ibase_blob_cancel', 'ibase_blob_close', 'ibase_blob_create', 'ibase_blob_get', 'ibase_blob_open', 'ibase_close', 'ibase_commit', 'ibase_commit_ret', 'ibase_connect', 'ibase_db_info', 'ibase_delete_user', 'ibase_drop_db', 'ibase_execute', 'ibase_fetch_assoc', 'ibase_fetch_object', 'ibase_fetch_row', 'ibase_field_info', 'ibase_free_event_handler', 'ibase_free_query', 'ibase_free_result', 'ibase_gen_id', 'ibase_maintain_db', 'ibase_modify_user', 'ibase_name_result', 'ibase_num_fields', 'ibase_num_params', 'ibase_param_info', 'ibase_pconnect', 'ibase_prepare', 'ibase_query', 'ibase_restore', 'ibase_rollback', 'ibase_rollback_ret', 'ibase_server_info', 'ibase_service_attach', 'ibase_service_detach', 'ibase_set_event_handler', 'ibase_trans', 'ifx_affected_rows', 'ifx_close', 'ifx_connect', 'ifx_do', 'ifx_error', 'ifx_fetch_row', 'ifx_fieldproperties', 'ifx_fieldtypes', 'ifx_free_result', 'ifx_getsqlca', 'ifx_htmltbl_result', 'ifx_num_fields', 'ifx_num_rows', 'ifx_pconnect', 'ifx_prepare', 'ifx_query', 'image2wbmp', 'imageaffine', 'imagealphablending', 'imageantialias', 'imagearc', 'imagebmp', 'imagechar', 'imagecharup', 'imagecolorallocate', 'imagecolorallocatealpha', 'imagecolorat', 'imagecolorclosest', 'imagecolorclosestalpha', 'imagecolorclosesthwb', 'imagecolordeallocate', 'imagecolorexact', 'imagecolorexactalpha', 'imagecolormatch', 'imagecolorresolve', 'imagecolorresolvealpha', 'imagecolorset', 'imagecolorsforindex', 'imagecolorstotal', 'imagecolortransparent', 'imageconvolution', 'imagecopy', 'imagecopymerge', 'imagecopymergegray', 'imagecopyresampled', 'imagecopyresized', 'imagecrop', 'imagecropauto', 'imagedashedline', 'imagedestroy', 'imageellipse', 'imagefill', 'imagefilledarc', 'imagefilledellipse', 'imagefilledpolygon', 'imagefilledrectangle', 'imagefilltoborder', 'imagefilter', 'imageflip', 'imagefttext', 'imagegammacorrect', 'imagegd', 'imagegd2', 'imagegetclip', 'imagegif', 'imagegrabscreen', 'imagegrabwindow', 'imageinterlace', 'imageistruecolor', 'imagejpeg', 'imagelayereffect', 'imageline', 'imageopenpolygon', 'imagepalettecopy', 'imagepalettetotruecolor', 'imagepng', 'imagepolygon', 'imagepsencodefont', 'imagepsextendfont', 'imagepsfreefont', 'imagepsloadfont', 'imagepsslantfont', 'imagepstext', 'imagerectangle', 'imageresolution', 'imagerotate', 'imagesavealpha', 'imagescale', 'imagesetbrush', 'imagesetclip', 'imagesetinterpolation', 'imagesetpixel', 'imagesetstyle', 'imagesetthickness', 'imagesettile', 'imagestring', 'imagestringup', 'imagesx', 'imagesy', 'imagetruecolortopalette', 'imagettftext', 'imagewbmp', 'imagewebp', 'imagexbm', 'imap_append', 'imap_body', 'imap_bodystruct', 'imap_check', 'imap_clearflag_full', 'imap_close', 'imap_create', 'imap_createmailbox', 'imap_delete', 'imap_deletemailbox', 'imap_expunge', 'imap_fetch_overview', 'imap_fetchbody', 'imap_fetchheader', 'imap_fetchmime', 'imap_fetchstructure', 'imap_fetchtext', 'imap_gc', 'imap_get_quota', 'imap_get_quotaroot', 'imap_getacl', 'imap_getmailboxes', 'imap_getsubscribed', 'imap_header', 'imap_headerinfo', 'imap_headers', 'imap_list', 'imap_listmailbox', 'imap_listscan', 'imap_listsubscribed', 'imap_lsub', 'imap_mail_copy', 'imap_mail_move', 'imap_mailboxmsginfo', 'imap_msgno', 'imap_num_msg', 'imap_num_recent', 'imap_ping', 'imap_rename', 'imap_renamemailbox', 'imap_reopen', 'imap_savebody', 'imap_scan', 'imap_scanmailbox', 'imap_search', 'imap_set_quota', 'imap_setacl', 'imap_setflag_full', 'imap_sort', 'imap_status', 'imap_subscribe', 'imap_thread', 'imap_uid', 'imap_undelete', 'imap_unsubscribe', 'inflate_add', 'inflate_get_read_len', 'inflate_get_status', 'ingres_autocommit', 'ingres_autocommit_state', 'ingres_charset', 'ingres_close', 'ingres_commit', 'ingres_connect', 'ingres_cursor', 'ingres_errno', 'ingres_error', 'ingres_errsqlstate', 'ingres_escape_string', 'ingres_execute', 'ingres_fetch_array', 'ingres_fetch_assoc', 'ingres_fetch_object', 'ingres_fetch_proc_return', 'ingres_fetch_row', 'ingres_field_length', 'ingres_field_name', 'ingres_field_nullable', 'ingres_field_precision', 'ingres_field_scale', 'ingres_field_type', 'ingres_free_result', 'ingres_next_error', 'ingres_num_fields', 'ingres_num_rows', 'ingres_pconnect', 'ingres_prepare', 'ingres_query', 'ingres_result_seek', 'ingres_rollback', 'ingres_set_environment', 'ingres_unbuffered_query', 'inotify_add_watch', 'inotify_init', 'inotify_queue_len', 'inotify_read', 'inotify_rm_watch', 'kadm5_chpass_principal', 'kadm5_create_principal', 'kadm5_delete_principal', 'kadm5_destroy', 'kadm5_flush', 'kadm5_get_policies', 'kadm5_get_principal', 'kadm5_get_principals', 'kadm5_init_with_password', 'kadm5_modify_principal', 'ldap_add', 'ldap_bind', 'ldap_close', 'ldap_compare', 'ldap_control_paged_result', 'ldap_control_paged_result_response', 'ldap_count_entries', 'ldap_delete', 'ldap_errno', 'ldap_error', 'ldap_exop', 'ldap_exop_passwd', 'ldap_exop_refresh', 'ldap_exop_whoami', 'ldap_first_attribute', 'ldap_first_entry', 'ldap_first_reference', 'ldap_free_result', 'ldap_get_attributes', 'ldap_get_dn', 'ldap_get_entries', 'ldap_get_option', 'ldap_get_values', 'ldap_get_values_len', 'ldap_mod_add', 'ldap_mod_del', 'ldap_mod_replace', 'ldap_modify', 'ldap_modify_batch', 'ldap_next_attribute', 'ldap_next_entry', 'ldap_next_reference', 'ldap_parse_exop', 'ldap_parse_reference', 'ldap_parse_result', 'ldap_rename', 'ldap_sasl_bind', 'ldap_set_option', 'ldap_set_rebind_proc', 'ldap_sort', 'ldap_start_tls', 'ldap_unbind', 'libxml_set_streams_context', 'm_checkstatus', 'm_completeauthorizations', 'm_connect', 'm_connectionerror', 'm_deletetrans', 'm_destroyconn', 'm_getcell', 'm_getcellbynum', 'm_getcommadelimited', 'm_getheader', 'm_initconn', 'm_iscommadelimited', 'm_maxconntimeout', 'm_monitor', 'm_numcolumns', 'm_numrows', 'm_parsecommadelimited', 'm_responsekeys', 'm_responseparam', 'm_returnstatus', 'm_setblocking', 'm_setdropfile', 'm_setip', 'm_setssl', 'm_setssl_cafile', 'm_setssl_files', 'm_settimeout', 'm_transactionssent', 'm_transinqueue', 'm_transkeyval', 'm_transnew', 'm_transsend', 'm_validateidentifier', 'm_verifyconnection', 'm_verifysslcert', 'mailparse_determine_best_xfer_encoding', 'mailparse_msg_create', 'mailparse_msg_extract_part', 'mailparse_msg_extract_part_file', 'mailparse_msg_extract_whole_part_file', 'mailparse_msg_free', 'mailparse_msg_get_part', 'mailparse_msg_get_part_data', 'mailparse_msg_get_structure', 'mailparse_msg_parse', 'mailparse_msg_parse_file', 'mailparse_stream_encode', 'mailparse_uudecode_all', 'maxdb::use_result', 'maxdb_affected_rows', 'maxdb_connect', 'maxdb_disable_rpl_parse', 'maxdb_dump_debug_info', 'maxdb_embedded_connect', 'maxdb_enable_reads_from_master', 'maxdb_enable_rpl_parse', 'maxdb_errno', 'maxdb_error', 'maxdb_fetch_lengths', 'maxdb_field_tell', 'maxdb_get_host_info', 'maxdb_get_proto_info', 'maxdb_get_server_info', 'maxdb_get_server_version', 'maxdb_info', 'maxdb_init', 'maxdb_insert_id', 'maxdb_master_query', 'maxdb_more_results', 'maxdb_next_result', 'maxdb_num_fields', 'maxdb_num_rows', 'maxdb_rpl_parse_enabled', 'maxdb_rpl_probe', 'maxdb_select_db', 'maxdb_sqlstate', 'maxdb_stmt::result_metadata', 'maxdb_stmt_affected_rows', 'maxdb_stmt_errno', 'maxdb_stmt_error', 'maxdb_stmt_num_rows', 'maxdb_stmt_param_count', 'maxdb_stmt_result_metadata', 'maxdb_stmt_sqlstate', 'maxdb_thread_id', 'maxdb_use_result', 'maxdb_warning_count', 'mcrypt_enc_get_algorithms_name', 'mcrypt_enc_get_block_size', 'mcrypt_enc_get_iv_size', 'mcrypt_enc_get_key_size', 'mcrypt_enc_get_modes_name', 'mcrypt_enc_get_supported_key_sizes', 'mcrypt_enc_is_block_algorithm', 'mcrypt_enc_is_block_algorithm_mode', 'mcrypt_enc_is_block_mode', 'mcrypt_enc_self_test', 'mcrypt_generic', 'mcrypt_generic_deinit', 'mcrypt_generic_end', 'mcrypt_generic_init', 'mcrypt_module_close', 'mcrypt_module_open', 'mdecrypt_generic', 'mkdir', 'mqseries_back', 'mqseries_begin', 'mqseries_close', 'mqseries_cmit', 'mqseries_conn', 'mqseries_connx', 'mqseries_disc', 'mqseries_get', 'mqseries_inq', 'mqseries_open', 'mqseries_put', 'mqseries_put1', 'mqseries_set', 'msg_get_queue', 'msg_receive', 'msg_remove_queue', 'msg_send', 'msg_set_queue', 'msg_stat_queue', 'msql_affected_rows', 'msql_close', 'msql_connect', 'msql_create_db', 'msql_data_seek', 'msql_db_query', 'msql_drop_db', 'msql_fetch_array', 'msql_fetch_field', 'msql_fetch_object', 'msql_fetch_row', 'msql_field_flags', 'msql_field_len', 'msql_field_name', 'msql_field_seek', 'msql_field_table', 'msql_field_type', 'msql_free_result', 'msql_list_dbs', 'msql_list_fields', 'msql_list_tables', 'msql_num_fields', 'msql_num_rows', 'msql_pconnect', 'msql_query', 'msql_result', 'msql_select_db', 'mssql_bind', 'mssql_close', 'mssql_connect', 'mssql_data_seek', 'mssql_execute', 'mssql_fetch_array', 'mssql_fetch_assoc', 'mssql_fetch_batch', 'mssql_fetch_field', 'mssql_fetch_object', 'mssql_fetch_row', 'mssql_field_length', 'mssql_field_name', 'mssql_field_seek', 'mssql_field_type', 'mssql_free_result', 'mssql_free_statement', 'mssql_init', 'mssql_next_result', 'mssql_num_fields', 'mssql_num_rows', 'mssql_pconnect', 'mssql_query', 'mssql_result', 'mssql_rows_affected', 'mssql_select_db', 'mysql_affected_rows', 'mysql_client_encoding', 'mysql_close', 'mysql_connect', 'mysql_create_db', 'mysql_data_seek', 'mysql_db_name', 'mysql_db_query', 'mysql_drop_db', 'mysql_errno', 'mysql_error', 'mysql_fetch_array', 'mysql_fetch_assoc', 'mysql_fetch_field', 'mysql_fetch_lengths', 'mysql_fetch_object', 'mysql_fetch_row', 'mysql_field_flags', 'mysql_field_len', 'mysql_field_name', 'mysql_field_seek', 'mysql_field_table', 'mysql_field_type', 'mysql_free_result', 'mysql_get_host_info', 'mysql_get_proto_info', 'mysql_get_server_info', 'mysql_info', 'mysql_insert_id', 'mysql_list_dbs', 'mysql_list_fields', 'mysql_list_processes', 'mysql_list_tables', 'mysql_num_fields', 'mysql_num_rows', 'mysql_pconnect', 'mysql_ping', 'mysql_query', 'mysql_real_escape_string', 'mysql_result', 'mysql_select_db', 'mysql_set_charset', 'mysql_stat', 'mysql_tablename', 'mysql_thread_id', 'mysql_unbuffered_query', 'mysqlnd_uh_convert_to_mysqlnd', 'ncurses_bottom_panel', 'ncurses_del_panel', 'ncurses_delwin', 'ncurses_getmaxyx', 'ncurses_getyx', 'ncurses_hide_panel', 'ncurses_keypad', 'ncurses_meta', 'ncurses_move_panel', 'ncurses_mvwaddstr', 'ncurses_new_panel', 'ncurses_newpad', 'ncurses_newwin', 'ncurses_panel_above', 'ncurses_panel_below', 'ncurses_panel_window', 'ncurses_pnoutrefresh', 'ncurses_prefresh', 'ncurses_replace_panel', 'ncurses_show_panel', 'ncurses_top_panel', 'ncurses_waddch', 'ncurses_waddstr', 'ncurses_wattroff', 'ncurses_wattron', 'ncurses_wattrset', 'ncurses_wborder', 'ncurses_wclear', 'ncurses_wcolor_set', 'ncurses_werase', 'ncurses_wgetch', 'ncurses_whline', 'ncurses_wmouse_trafo', 'ncurses_wmove', 'ncurses_wnoutrefresh', 'ncurses_wrefresh', 'ncurses_wstandend', 'ncurses_wstandout', 'ncurses_wvline', 'newt_button', 'newt_button_bar', 'newt_checkbox', 'newt_checkbox_get_value', 'newt_checkbox_set_flags', 'newt_checkbox_set_value', 'newt_checkbox_tree', 'newt_checkbox_tree_add_item', 'newt_checkbox_tree_find_item', 'newt_checkbox_tree_get_current', 'newt_checkbox_tree_get_entry_value', 'newt_checkbox_tree_get_multi_selection', 'newt_checkbox_tree_get_selection', 'newt_checkbox_tree_multi', 'newt_checkbox_tree_set_current', 'newt_checkbox_tree_set_entry', 'newt_checkbox_tree_set_entry_value', 'newt_checkbox_tree_set_width', 'newt_compact_button', 'newt_component_add_callback', 'newt_component_takes_focus', 'newt_create_grid', 'newt_draw_form', 'newt_entry', 'newt_entry_get_value', 'newt_entry_set', 'newt_entry_set_filter', 'newt_entry_set_flags', 'newt_form', 'newt_form_add_component', 'newt_form_add_components', 'newt_form_add_hot_key', 'newt_form_destroy', 'newt_form_get_current', 'newt_form_run', 'newt_form_set_background', 'newt_form_set_height', 'newt_form_set_size', 'newt_form_set_timer', 'newt_form_set_width', 'newt_form_watch_fd', 'newt_grid_add_components_to_form', 'newt_grid_basic_window', 'newt_grid_free', 'newt_grid_get_size', 'newt_grid_h_close_stacked', 'newt_grid_h_stacked', 'newt_grid_place', 'newt_grid_set_field', 'newt_grid_simple_window', 'newt_grid_v_close_stacked', 'newt_grid_v_stacked', 'newt_grid_wrapped_window', 'newt_grid_wrapped_window_at', 'newt_label', 'newt_label_set_text', 'newt_listbox', 'newt_listbox_append_entry', 'newt_listbox_clear', 'newt_listbox_clear_selection', 'newt_listbox_delete_entry', 'newt_listbox_get_current', 'newt_listbox_get_selection', 'newt_listbox_insert_entry', 'newt_listbox_item_count', 'newt_listbox_select_item', 'newt_listbox_set_current', 'newt_listbox_set_current_by_key', 'newt_listbox_set_data', 'newt_listbox_set_entry', 'newt_listbox_set_width', 'newt_listitem', 'newt_listitem_get_data', 'newt_listitem_set', 'newt_radio_get_current', 'newt_radiobutton', 'newt_run_form', 'newt_scale', 'newt_scale_set', 'newt_scrollbar_set', 'newt_textbox', 'newt_textbox_get_num_lines', 'newt_textbox_reflowed', 'newt_textbox_set_height', 'newt_textbox_set_text', 'newt_vertical_scrollbar', 'oci_bind_array_by_name', 'oci_bind_by_name', 'oci_cancel', 'oci_close', 'oci_commit', 'oci_connect', 'oci_define_by_name', 'oci_error', 'oci_execute', 'oci_fetch', 'oci_fetch_all', 'oci_fetch_array', 'oci_fetch_assoc', 'oci_fetch_object', 'oci_fetch_row', 'oci_field_is_null', 'oci_field_name', 'oci_field_precision', 'oci_field_scale', 'oci_field_size', 'oci_field_type', 'oci_field_type_raw', 'oci_free_cursor', 'oci_free_statement', 'oci_get_implicit_resultset', 'oci_new_collection', 'oci_new_connect', 'oci_new_cursor', 'oci_new_descriptor', 'oci_num_fields', 'oci_num_rows', 'oci_parse', 'oci_pconnect', 'oci_register_taf_callback', 'oci_result', 'oci_rollback', 'oci_server_version', 'oci_set_action', 'oci_set_client_identifier', 'oci_set_client_info', 'oci_set_module_name', 'oci_set_prefetch', 'oci_statement_type', 'oci_unregister_taf_callback', 'odbc_autocommit', 'odbc_close', 'odbc_columnprivileges', 'odbc_columns', 'odbc_commit', 'odbc_connect', 'odbc_cursor', 'odbc_data_source', 'odbc_do', 'odbc_error', 'odbc_errormsg', 'odbc_exec', 'odbc_execute', 'odbc_fetch_array', 'odbc_fetch_into', 'odbc_fetch_row', 'odbc_field_len', 'odbc_field_name', 'odbc_field_num', 'odbc_field_precision', 'odbc_field_scale', 'odbc_field_type', 'odbc_foreignkeys', 'odbc_free_result', 'odbc_gettypeinfo', 'odbc_next_result', 'odbc_num_fields', 'odbc_num_rows', 'odbc_pconnect', 'odbc_prepare', 'odbc_primarykeys', 'odbc_procedurecolumns', 'odbc_procedures', 'odbc_result', 'odbc_result_all', 'odbc_rollback', 'odbc_setoption', 'odbc_specialcolumns', 'odbc_statistics', 'odbc_tableprivileges', 'odbc_tables', 'openal_buffer_create', 'openal_buffer_data', 'openal_buffer_destroy', 'openal_buffer_get', 'openal_buffer_loadwav', 'openal_context_create', 'openal_context_current', 'openal_context_destroy', 'openal_context_process', 'openal_context_suspend', 'openal_device_close', 'openal_device_open', 'openal_source_create', 'openal_source_destroy', 'openal_source_get', 'openal_source_pause', 'openal_source_play', 'openal_source_rewind', 'openal_source_set', 'openal_source_stop', 'openal_stream', 'opendir', 'openssl_csr_new', 'openssl_dh_compute_key', 'openssl_free_key', 'openssl_pkey_export', 'openssl_pkey_free', 'openssl_pkey_get_details', 'openssl_spki_new', 'openssl_x509_free', 'pclose', 'pfsockopen', 'pg_affected_rows', 'pg_cancel_query', 'pg_client_encoding', 'pg_close', 'pg_connect_poll', 'pg_connection_busy', 'pg_connection_reset', 'pg_connection_status', 'pg_consume_input', 'pg_convert', 'pg_copy_from', 'pg_copy_to', 'pg_dbname', 'pg_delete', 'pg_end_copy', 'pg_escape_bytea', 'pg_escape_identifier', 'pg_escape_literal', 'pg_escape_string', 'pg_execute', 'pg_fetch_all', 'pg_fetch_all_columns', 'pg_fetch_array', 'pg_fetch_assoc', 'pg_fetch_row', 'pg_field_name', 'pg_field_num', 'pg_field_size', 'pg_field_table', 'pg_field_type', 'pg_field_type_oid', 'pg_flush', 'pg_free_result', 'pg_get_notify', 'pg_get_pid', 'pg_get_result', 'pg_host', 'pg_insert', 'pg_last_error', 'pg_last_notice', 'pg_last_oid', 'pg_lo_close', 'pg_lo_create', 'pg_lo_export', 'pg_lo_import', 'pg_lo_open', 'pg_lo_read', 'pg_lo_read_all', 'pg_lo_seek', 'pg_lo_tell', 'pg_lo_truncate', 'pg_lo_unlink', 'pg_lo_write', 'pg_meta_data', 'pg_num_fields', 'pg_num_rows', 'pg_options', 'pg_parameter_status', 'pg_ping', 'pg_port', 'pg_prepare', 'pg_put_line', 'pg_query', 'pg_query_params', 'pg_result_error', 'pg_result_error_field', 'pg_result_seek', 'pg_result_status', 'pg_select', 'pg_send_execute', 'pg_send_prepare', 'pg_send_query', 'pg_send_query_params', 'pg_set_client_encoding', 'pg_set_error_verbosity', 'pg_socket', 'pg_trace', 'pg_transaction_status', 'pg_tty', 'pg_untrace', 'pg_update', 'pg_version', 'php_user_filter::filter', 'proc_close', 'proc_get_status', 'proc_terminate', 'ps_add_bookmark', 'ps_add_launchlink', 'ps_add_locallink', 'ps_add_note', 'ps_add_pdflink', 'ps_add_weblink', 'ps_arc', 'ps_arcn', 'ps_begin_page', 'ps_begin_pattern', 'ps_begin_template', 'ps_circle', 'ps_clip', 'ps_close', 'ps_close_image', 'ps_closepath', 'ps_closepath_stroke', 'ps_continue_text', 'ps_curveto', 'ps_delete', 'ps_end_page', 'ps_end_pattern', 'ps_end_template', 'ps_fill', 'ps_fill_stroke', 'ps_findfont', 'ps_get_buffer', 'ps_get_parameter', 'ps_get_value', 'ps_hyphenate', 'ps_include_file', 'ps_lineto', 'ps_makespotcolor', 'ps_moveto', 'ps_new', 'ps_open_file', 'ps_open_image', 'ps_open_image_file', 'ps_open_memory_image', 'ps_place_image', 'ps_rect', 'ps_restore', 'ps_rotate', 'ps_save', 'ps_scale', 'ps_set_border_color', 'ps_set_border_dash', 'ps_set_border_style', 'ps_set_info', 'ps_set_parameter', 'ps_set_text_pos', 'ps_set_value', 'ps_setcolor', 'ps_setdash', 'ps_setflat', 'ps_setfont', 'ps_setgray', 'ps_setlinecap', 'ps_setlinejoin', 'ps_setlinewidth', 'ps_setmiterlimit', 'ps_setoverprintmode', 'ps_setpolydash', 'ps_shading', 'ps_shading_pattern', 'ps_shfill', 'ps_show', 'ps_show2', 'ps_show_boxed', 'ps_show_xy', 'ps_show_xy2', 'ps_string_geometry', 'ps_stringwidth', 'ps_stroke', 'ps_symbol', 'ps_symbol_name', 'ps_symbol_width', 'ps_translate', 'px_close', 'px_create_fp', 'px_date2string', 'px_delete', 'px_delete_record', 'px_get_field', 'px_get_info', 'px_get_parameter', 'px_get_record', 'px_get_schema', 'px_get_value', 'px_insert_record', 'px_new', 'px_numfields', 'px_numrecords', 'px_open_fp', 'px_put_record', 'px_retrieve_record', 'px_set_blob_file', 'px_set_parameter', 'px_set_tablename', 'px_set_targetencoding', 'px_set_value', 'px_timestamp2string', 'px_update_record', 'radius_acct_open', 'radius_add_server', 'radius_auth_open', 'radius_close', 'radius_config', 'radius_create_request', 'radius_demangle', 'radius_demangle_mppe_key', 'radius_get_attr', 'radius_put_addr', 'radius_put_attr', 'radius_put_int', 'radius_put_string', 'radius_put_vendor_addr', 'radius_put_vendor_attr', 'radius_put_vendor_int', 'radius_put_vendor_string', 'radius_request_authenticator', 'radius_salt_encrypt_attr', 'radius_send_request', 'radius_server_secret', 'radius_strerror', 'readdir', 'readfile', 'recode_file', 'rename', 'rewind', 'rewinddir', 'rmdir', 'rpm_close', 'rpm_get_tag', 'rpm_open', 'sapi_windows_vt100_support', 'scandir', 'sem_acquire', 'sem_get', 'sem_release', 'sem_remove', 'set_file_buffer', 'shm_attach', 'shm_detach', 'shm_get_var', 'shm_has_var', 'shm_put_var', 'shm_remove', 'shm_remove_var', 'shmop_close', 'shmop_delete', 'shmop_open', 'shmop_read', 'shmop_size', 'shmop_write', 'socket_accept', 'socket_addrinfo_bind', 'socket_addrinfo_connect', 'socket_addrinfo_explain', 'socket_bind', 'socket_clear_error', 'socket_close', 'socket_connect', 'socket_export_stream', 'socket_get_option', 'socket_get_status', 'socket_getopt', 'socket_getpeername', 'socket_getsockname', 'socket_import_stream', 'socket_last_error', 'socket_listen', 'socket_read', 'socket_recv', 'socket_recvfrom', 'socket_recvmsg', 'socket_send', 'socket_sendmsg', 'socket_sendto', 'socket_set_block', 'socket_set_blocking', 'socket_set_nonblock', 'socket_set_option', 'socket_set_timeout', 'socket_shutdown', 'socket_write', 'sqlite_close', 'sqlite_fetch_string', 'sqlite_has_more', 'sqlite_open', 'sqlite_popen', 'sqlsrv_begin_transaction', 'sqlsrv_cancel', 'sqlsrv_client_info', 'sqlsrv_close', 'sqlsrv_commit', 'sqlsrv_connect', 'sqlsrv_execute', 'sqlsrv_fetch', 'sqlsrv_fetch_array', 'sqlsrv_fetch_object', 'sqlsrv_field_metadata', 'sqlsrv_free_stmt', 'sqlsrv_get_field', 'sqlsrv_has_rows', 'sqlsrv_next_result', 'sqlsrv_num_fields', 'sqlsrv_num_rows', 'sqlsrv_prepare', 'sqlsrv_query', 'sqlsrv_rollback', 'sqlsrv_rows_affected', 'sqlsrv_send_stream_data', 'sqlsrv_server_info', 'ssh2_auth_agent', 'ssh2_auth_hostbased_file', 'ssh2_auth_none', 'ssh2_auth_password', 'ssh2_auth_pubkey_file', 'ssh2_disconnect', 'ssh2_exec', 'ssh2_fetch_stream', 'ssh2_fingerprint', 'ssh2_methods_negotiated', 'ssh2_publickey_add', 'ssh2_publickey_init', 'ssh2_publickey_list', 'ssh2_publickey_remove', 'ssh2_scp_recv', 'ssh2_scp_send', 'ssh2_sftp', 'ssh2_sftp_chmod', 'ssh2_sftp_lstat', 'ssh2_sftp_mkdir', 'ssh2_sftp_readlink', 'ssh2_sftp_realpath', 'ssh2_sftp_rename', 'ssh2_sftp_rmdir', 'ssh2_sftp_stat', 'ssh2_sftp_symlink', 'ssh2_sftp_unlink', 'ssh2_shell', 'ssh2_tunnel', 'stomp_connect', 'streamWrapper::stream_cast', 'stream_bucket_append', 'stream_bucket_make_writeable', 'stream_bucket_new', 'stream_bucket_prepend', 'stream_context_create', 'stream_context_get_default', 'stream_context_get_options', 'stream_context_get_params', 'stream_context_set_default', 'stream_context_set_params', 'stream_copy_to_stream', 'stream_encoding', 'stream_filter_append', 'stream_filter_prepend', 'stream_filter_remove', 'stream_get_contents', 'stream_get_line', 'stream_get_meta_data', 'stream_isatty', 'stream_set_blocking', 'stream_set_chunk_size', 'stream_set_read_buffer', 'stream_set_timeout', 'stream_set_write_buffer', 'stream_socket_accept', 'stream_socket_client', 'stream_socket_enable_crypto', 'stream_socket_get_name', 'stream_socket_recvfrom', 'stream_socket_sendto', 'stream_socket_server', 'stream_socket_shutdown', 'stream_supports_lock', 'svn_fs_abort_txn', 'svn_fs_apply_text', 'svn_fs_begin_txn2', 'svn_fs_change_node_prop', 'svn_fs_check_path', 'svn_fs_contents_changed', 'svn_fs_copy', 'svn_fs_delete', 'svn_fs_dir_entries', 'svn_fs_file_contents', 'svn_fs_file_length', 'svn_fs_is_dir', 'svn_fs_is_file', 'svn_fs_make_dir', 'svn_fs_make_file', 'svn_fs_node_created_rev', 'svn_fs_node_prop', 'svn_fs_props_changed', 'svn_fs_revision_prop', 'svn_fs_revision_root', 'svn_fs_txn_root', 'svn_fs_youngest_rev', 'svn_repos_create', 'svn_repos_fs', 'svn_repos_fs_begin_txn_for_commit', 'svn_repos_fs_commit_txn', 'svn_repos_open', 'sybase_affected_rows', 'sybase_close', 'sybase_connect', 'sybase_data_seek', 'sybase_fetch_array', 'sybase_fetch_assoc', 'sybase_fetch_field', 'sybase_fetch_object', 'sybase_fetch_row', 'sybase_field_seek', 'sybase_free_result', 'sybase_num_fields', 'sybase_num_rows', 'sybase_pconnect', 'sybase_query', 'sybase_result', 'sybase_select_db', 'sybase_set_message_handler', 'sybase_unbuffered_query', 'tmpfile', 'udm_add_search_limit', 'udm_alloc_agent', 'udm_alloc_agent_array', 'udm_cat_list', 'udm_cat_path', 'udm_check_charset', 'udm_clear_search_limits', 'udm_crc32', 'udm_errno', 'udm_error', 'udm_find', 'udm_free_agent', 'udm_free_res', 'udm_get_doc_count', 'udm_get_res_field', 'udm_get_res_param', 'udm_hash32', 'udm_load_ispell_data', 'udm_set_agent_param', 'unlink', 'vfprintf', 'w32api_init_dtype', 'wddx_add_vars', 'wddx_packet_end', 'wddx_packet_start', 'xml_get_current_byte_index', 'xml_get_current_column_number', 'xml_get_current_line_number', 'xml_get_error_code', 'xml_parse', 'xml_parse_into_struct', 'xml_parser_create', 'xml_parser_create_ns', 'xml_parser_free', 'xml_parser_get_option', 'xml_parser_set_option', 'xml_set_character_data_handler', 'xml_set_default_handler', 'xml_set_element_handler', 'xml_set_end_namespace_decl_handler', 'xml_set_external_entity_ref_handler', 'xml_set_notation_decl_handler', 'xml_set_object', 'xml_set_processing_instruction_handler', 'xml_set_start_namespace_decl_handler', 'xml_set_unparsed_entity_decl_handler', 'xmlrpc_server_add_introspection_data', 'xmlrpc_server_call_method', 'xmlrpc_server_create', 'xmlrpc_server_destroy', 'xmlrpc_server_register_introspection_callback', 'xmlrpc_server_register_method', 'xmlwriter_end_attribute', 'xmlwriter_end_cdata', 'xmlwriter_end_comment', 'xmlwriter_end_document', 'xmlwriter_end_dtd', 'xmlwriter_end_dtd_attlist', 'xmlwriter_end_dtd_element', 'xmlwriter_end_dtd_entity', 'xmlwriter_end_element', 'xmlwriter_end_pi', 'xmlwriter_flush', 'xmlwriter_full_end_element', 'xmlwriter_open_memory', 'xmlwriter_open_uri', 'xmlwriter_output_memory', 'xmlwriter_set_indent', 'xmlwriter_set_indent_string', 'xmlwriter_start_attribute', 'xmlwriter_start_attribute_ns', 'xmlwriter_start_cdata', 'xmlwriter_start_comment', 'xmlwriter_start_document', 'xmlwriter_start_dtd', 'xmlwriter_start_dtd_attlist', 'xmlwriter_start_dtd_element', 'xmlwriter_start_dtd_entity', 'xmlwriter_start_element', 'xmlwriter_start_element_ns', 'xmlwriter_start_pi', 'xmlwriter_text', 'xmlwriter_write_attribute', 'xmlwriter_write_attribute_ns', 'xmlwriter_write_cdata', 'xmlwriter_write_comment', 'xmlwriter_write_dtd', 'xmlwriter_write_dtd_attlist', 'xmlwriter_write_dtd_element', 'xmlwriter_write_dtd_entity', 'xmlwriter_write_element', 'xmlwriter_write_element_ns', 'xmlwriter_write_pi', 'xmlwriter_write_raw', 'xslt_create', 'yaz_addinfo', 'yaz_ccl_conf', 'yaz_ccl_parse', 'yaz_close', 'yaz_database', 'yaz_element', 'yaz_errno', 'yaz_error', 'yaz_es', 'yaz_es_result', 'yaz_get_option', 'yaz_hits', 'yaz_itemorder', 'yaz_present', 'yaz_range', 'yaz_record', 'yaz_scan', 'yaz_scan_result', 'yaz_schema', 'yaz_search', 'yaz_sort', 'yaz_syntax', 'zip_close', 'zip_entry_close', 'zip_entry_compressedsize', 'zip_entry_compressionmethod', 'zip_entry_filesize', 'zip_entry_name', 'zip_entry_open', 'zip_entry_read', 'zip_open', 'zip_read', ]; } } Resource Operations Copyright (c) 2015-2018, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class VersionConstraintParser { /** * @param string $value * * @return VersionConstraint * * @throws UnsupportedVersionConstraintException */ public function parse($value) { if (strpos($value, '||') !== false) { return $this->handleOrGroup($value); } if (!preg_match('/^[\^~\*]?[\d.\*]+(?:-.*)?$/', $value)) { throw new UnsupportedVersionConstraintException( sprintf('Version constraint %s is not supported.', $value) ); } switch ($value[0]) { case '~': return $this->handleTildeOperator($value); case '^': return $this->handleCaretOperator($value); } $version = new VersionConstraintValue($value); if ($version->getMajor()->isAny()) { return new AnyVersionConstraint(); } if ($version->getMinor()->isAny()) { return new SpecificMajorVersionConstraint( $version->getVersionString(), $version->getMajor()->getValue() ); } if ($version->getPatch()->isAny()) { return new SpecificMajorAndMinorVersionConstraint( $version->getVersionString(), $version->getMajor()->getValue(), $version->getMinor()->getValue() ); } return new ExactVersionConstraint($version->getVersionString()); } /** * @param $value * * @return OrVersionConstraintGroup */ private function handleOrGroup($value) { $constraints = []; foreach (explode('||', $value) as $groupSegment) { $constraints[] = $this->parse(trim($groupSegment)); } return new OrVersionConstraintGroup($value, $constraints); } /** * @param string $value * * @return AndVersionConstraintGroup */ private function handleTildeOperator($value) { $version = new Version(substr($value, 1)); $constraints = [ new GreaterThanOrEqualToVersionConstraint($value, $version) ]; if ($version->getPatch()->isAny()) { $constraints[] = new SpecificMajorVersionConstraint( $value, $version->getMajor()->getValue() ); } else { $constraints[] = new SpecificMajorAndMinorVersionConstraint( $value, $version->getMajor()->getValue(), $version->getMinor()->getValue() ); } return new AndVersionConstraintGroup($value, $constraints); } /** * @param string $value * * @return AndVersionConstraintGroup */ private function handleCaretOperator($value) { $version = new Version(substr($value, 1)); return new AndVersionConstraintGroup( $value, [ new GreaterThanOrEqualToVersionConstraint($value, $version), new SpecificMajorVersionConstraint($value, $version->getMajor()->getValue()) ] ); } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class Version { /** * @var VersionNumber */ private $major; /** * @var VersionNumber */ private $minor; /** * @var VersionNumber */ private $patch; /** * @var PreReleaseSuffix */ private $preReleaseSuffix; /** * @var string */ private $versionString = ''; /** * @param string $versionString */ public function __construct($versionString) { $this->ensureVersionStringIsValid($versionString); $this->versionString = $versionString; } /** * @return PreReleaseSuffix */ public function getPreReleaseSuffix() { return $this->preReleaseSuffix; } /** * @return string */ public function getVersionString() { return $this->versionString; } /** * @return bool */ public function hasPreReleaseSuffix() { return $this->preReleaseSuffix !== null; } /** * @param Version $version * * @return bool */ public function isGreaterThan(Version $version) { if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { return false; } if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) { return true; } if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) { return false; } if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) { return true; } if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) { return false; } if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) { return true; } if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { return false; } if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { return true; } if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) { return false; } return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); } /** * @return VersionNumber */ public function getMajor() { return $this->major; } /** * @return VersionNumber */ public function getMinor() { return $this->minor; } /** * @return VersionNumber */ public function getPatch() { return $this->patch; } /** * @param array $matches */ private function parseVersion(array $matches) { $this->major = new VersionNumber($matches['Major']); $this->minor = new VersionNumber($matches['Minor']); $this->patch = isset($matches['Patch']) ? new VersionNumber($matches['Patch']) : new VersionNumber(null); if (isset($matches['PreReleaseSuffix'])) { $this->preReleaseSuffix = new PreReleaseSuffix($matches['PreReleaseSuffix']); } } /** * @param string $version * * @throws InvalidVersionException */ private function ensureVersionStringIsValid($version) { $regex = '/^v? (?(0|(?:[1-9][0-9]*))) \\. (?(0|(?:[1-9][0-9]*))) (\\. (?(0|(?:[1-9][0-9]*))) )? (?: - (?(?:(dev|beta|b|RC|alpha|a|patch|p)\.?\d*)) )? $/x'; if (preg_match($regex, $version, $matches) !== 1) { throw new InvalidVersionException( sprintf("Version string '%s' does not follow SemVer semantics", $version) ); } $this->parseVersion($matches); } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; interface Exception { } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; final class UnsupportedVersionConstraintException extends \RuntimeException implements Exception { } 0, 'a' => 1, 'alpha' => 1, 'b' => 2, 'beta' => 2, 'rc' => 3, 'p' => 4, 'patch' => 4, ]; /** * @var string */ private $value; /** * @var int */ private $valueScore; /** * @var int */ private $number = 0; /** * @param string $value */ public function __construct($value) { $this->parseValue($value); } /** * @return string */ public function getValue() { return $this->value; } /** * @return int|null */ public function getNumber() { return $this->number; } /** * @param PreReleaseSuffix $suffix * * @return bool */ public function isGreaterThan(PreReleaseSuffix $suffix) { if ($this->valueScore > $suffix->valueScore) { return true; } if ($this->valueScore < $suffix->valueScore) { return false; } return $this->getNumber() > $suffix->getNumber(); } /** * @param $value * * @return int */ private function mapValueToScore($value) { if (array_key_exists($value, $this->valueScoreMap)) { return $this->valueScoreMap[$value]; } return 0; } private function parseValue($value) { $regex = '/-?(dev|beta|b|rc|alpha|a|patch|p)\.?(\d*).*$/i'; if (preg_match($regex, $value, $matches) !== 1) { throw new InvalidPreReleaseSuffixException(sprintf('Invalid label %s', $value)); } $this->value = $matches[1]; if (isset($matches[2])) { $this->number = (int)$matches[2]; } $this->valueScore = $this->mapValueToScore($this->value); } } versionString = $versionString; $this->parseVersion($versionString); } /** * @return string */ public function getLabel() { return $this->label; } /** * @return string */ public function getBuildMetaData() { return $this->buildMetaData; } /** * @return string */ public function getVersionString() { return $this->versionString; } /** * @return VersionNumber */ public function getMajor() { return $this->major; } /** * @return VersionNumber */ public function getMinor() { return $this->minor; } /** * @return VersionNumber */ public function getPatch() { return $this->patch; } /** * @param $versionString */ private function parseVersion($versionString) { $this->extractBuildMetaData($versionString); $this->extractLabel($versionString); $versionSegments = explode('.', $versionString); $this->major = new VersionNumber($versionSegments[0]); $minorValue = isset($versionSegments[1]) ? $versionSegments[1] : null; $patchValue = isset($versionSegments[2]) ? $versionSegments[2] : null; $this->minor = new VersionNumber($minorValue); $this->patch = new VersionNumber($patchValue); } /** * @param string $versionString */ private function extractBuildMetaData(&$versionString) { if (preg_match('/\+(.*)/', $versionString, $matches) == 1) { $this->buildMetaData = $matches[1]; $versionString = str_replace($matches[0], '', $versionString); } } /** * @param string $versionString */ private function extractLabel(&$versionString) { if (preg_match('/\-(.*)/', $versionString, $matches) == 1) { $this->label = $matches[1]; $versionString = str_replace($matches[0], '', $versionString); } } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; interface VersionConstraint { /** * @param Version $version * * @return bool */ public function complies(Version $version); /** * @return string */ public function asString(); } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; abstract class AbstractVersionConstraint implements VersionConstraint { /** * @var string */ private $originalValue = ''; /** * @param string $originalValue */ public function __construct($originalValue) { $this->originalValue = $originalValue; } /** * @return string */ public function asString() { return $this->originalValue; } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class SpecificMajorVersionConstraint extends AbstractVersionConstraint { /** * @var int */ private $major = 0; /** * @param string $originalValue * @param int $major */ public function __construct($originalValue, $major) { parent::__construct($originalValue); $this->major = $major; } /** * @param Version $version * * @return bool */ public function complies(Version $version) { return $version->getMajor()->getValue() == $this->major; } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class ExactVersionConstraint extends AbstractVersionConstraint { /** * @param Version $version * * @return bool */ public function complies(Version $version) { return $this->asString() == $version->getVersionString(); } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class GreaterThanOrEqualToVersionConstraint extends AbstractVersionConstraint { /** * @var Version */ private $minimalVersion; /** * @param string $originalValue * @param Version $minimalVersion */ public function __construct($originalValue, Version $minimalVersion) { parent::__construct($originalValue); $this->minimalVersion = $minimalVersion; } /** * @param Version $version * * @return bool */ public function complies(Version $version) { return $version->getVersionString() == $this->minimalVersion->getVersionString() || $version->isGreaterThan($this->minimalVersion); } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class AnyVersionConstraint implements VersionConstraint { /** * @param Version $version * * @return bool */ public function complies(Version $version) { return true; } /** * @return string */ public function asString() { return '*'; } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class OrVersionConstraintGroup extends AbstractVersionConstraint { /** * @var VersionConstraint[] */ private $constraints = []; /** * @param string $originalValue * @param VersionConstraint[] $constraints */ public function __construct($originalValue, array $constraints) { parent::__construct($originalValue); $this->constraints = $constraints; } /** * @param Version $version * * @return bool */ public function complies(Version $version) { foreach ($this->constraints as $constraint) { if ($constraint->complies($version)) { return true; } } return false; } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class AndVersionConstraintGroup extends AbstractVersionConstraint { /** * @var VersionConstraint[] */ private $constraints = []; /** * @param string $originalValue * @param VersionConstraint[] $constraints */ public function __construct($originalValue, array $constraints) { parent::__construct($originalValue); $this->constraints = $constraints; } /** * @param Version $version * * @return bool */ public function complies(Version $version) { foreach ($this->constraints as $constraint) { if (!$constraint->complies($version)) { return false; } } return true; } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class SpecificMajorAndMinorVersionConstraint extends AbstractVersionConstraint { /** * @var int */ private $major = 0; /** * @var int */ private $minor = 0; /** * @param string $originalValue * @param int $major * @param int $minor */ public function __construct($originalValue, $major, $minor) { parent::__construct($originalValue); $this->major = $major; $this->minor = $minor; } /** * @param Version $version * * @return bool */ public function complies(Version $version) { if ($version->getMajor()->getValue() != $this->major) { return false; } return $version->getMinor()->getValue() == $this->minor; } } , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PharIo\Version; class VersionNumber { /** * @var int */ private $value; /** * @param mixed $value */ public function __construct($value) { if (is_numeric($value)) { $this->value = $value; } } /** * @return bool */ public function isAny() { return $this->value === null; } /** * @return int */ public function getValue() { return $this->value; } } phar-io/version Copyright (c) 2016-2017 Arne Blankerts , Sebastian Heuer and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Arne Blankerts nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Invoker; final class TimeoutException extends \RuntimeException implements Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Invoker; interface Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Invoker; final class Invoker { /** * @var int */ private $timeout; /** * @throws \Throwable */ public function invoke(callable $callable, array $arguments, int $timeout) { \pcntl_signal( \SIGALRM, function (): void { throw new TimeoutException( \sprintf( 'Execution aborted after %d second%s', $this->timeout, $this->timeout === 1 ? '' : 's' ) ); }, true ); $this->timeout = $timeout; \pcntl_async_signals(true); \pcntl_alarm($timeout); try { $result = \call_user_func_array($callable, $arguments); } catch (\Throwable $t) { \pcntl_alarm(0); throw $t; } \pcntl_alarm(0); return $result; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ObjectEnumerator; interface Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ObjectEnumerator; class InvalidArgumentException extends \InvalidArgumentException implements Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ObjectEnumerator; use SebastianBergmann\ObjectReflector\ObjectReflector; use SebastianBergmann\RecursionContext\Context; /** * Traverses array structures and object graphs * to enumerate all referenced objects. */ class Enumerator { /** * Returns an array of all objects referenced either * directly or indirectly by a variable. * * @param array|object $variable * * @return object[] */ public function enumerate($variable) { if (!is_array($variable) && !is_object($variable)) { throw new InvalidArgumentException; } if (isset(func_get_args()[1])) { if (!func_get_args()[1] instanceof Context) { throw new InvalidArgumentException; } $processed = func_get_args()[1]; } else { $processed = new Context; } $objects = []; if ($processed->contains($variable)) { return $objects; } $array = $variable; $processed->add($variable); if (is_array($variable)) { foreach ($array as $element) { if (!is_array($element) && !is_object($element)) { continue; } $objects = array_merge( $objects, $this->enumerate($element, $processed) ); } } else { $objects[] = $variable; $reflector = new ObjectReflector; foreach ($reflector->getAttributes($variable) as $value) { if (!is_array($value) && !is_object($value)) { continue; } $objects = array_merge( $objects, $this->enumerate($value, $processed) ); } } return $objects; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Environment; final class Console { /** * @var int */ public const STDIN = 0; /** * @var int */ public const STDOUT = 1; /** * @var int */ public const STDERR = 2; /** * Returns true if STDOUT supports colorization. * * This code has been copied and adapted from * Symfony\Component\Console\Output\StreamOutput. */ public function hasColorSupport(): bool { if ('Hyper' === \getenv('TERM_PROGRAM')) { return true; } if ($this->isWindows()) { // @codeCoverageIgnoreStart return (\defined('STDOUT') && \function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(\STDOUT)) || false !== \getenv('ANSICON') || 'ON' === \getenv('ConEmuANSI') || 'xterm' === \getenv('TERM'); // @codeCoverageIgnoreEnd } if (!\defined('STDOUT')) { // @codeCoverageIgnoreStart return false; // @codeCoverageIgnoreEnd } if ($this->isInteractive(\STDOUT)) { return true; } $stat = @\fstat(\STDOUT); // Check if formatted mode is S_IFCHR return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } /** * Returns the number of columns of the terminal. * * @codeCoverageIgnore */ public function getNumberOfColumns(): int { if ($this->isWindows()) { return $this->getNumberOfColumnsWindows(); } if (!$this->isInteractive(\defined('STDIN') ? \STDIN : self::STDIN)) { return 80; } return $this->getNumberOfColumnsInteractive(); } /** * Returns if the file descriptor is an interactive terminal or not. * * Normally, we want to use a resource as a parameter, yet sadly it's not always awailable, * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. * * @param int|resource $fileDescriptor */ public function isInteractive($fileDescriptor = self::STDOUT): bool { return (\is_resource($fileDescriptor) && \function_exists('stream_isatty') && @\stream_isatty($fileDescriptor)) // stream_isatty requires that descriptor is a real resource, not numeric ID of it || (\function_exists('posix_isatty') && @\posix_isatty($fileDescriptor)); } private function isWindows(): bool { return \DIRECTORY_SEPARATOR === '\\'; } /** * @codeCoverageIgnore */ private function getNumberOfColumnsInteractive(): int { if (\function_exists('shell_exec') && \preg_match('#\d+ (\d+)#', \shell_exec('stty size') ?: '', $match) === 1) { if ((int) $match[1] > 0) { return (int) $match[1]; } } if (\function_exists('shell_exec') && \preg_match('#columns = (\d+);#', \shell_exec('stty') ?: '', $match) === 1) { if ((int) $match[1] > 0) { return (int) $match[1]; } } return 80; } /** * @codeCoverageIgnore */ private function getNumberOfColumnsWindows(): int { $ansicon = \getenv('ANSICON'); $columns = 80; if (\is_string($ansicon) && \preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', \trim($ansicon), $matches)) { $columns = $matches[1]; } elseif (\function_exists('proc_open')) { $process = \proc_open( 'mode CON', [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes, null, null, ['suppress_errors' => true] ); if (\is_resource($process)) { $info = \stream_get_contents($pipes[1]); \fclose($pipes[1]); \fclose($pipes[2]); \proc_close($process); if (\preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { $columns = $matches[2]; } } } return $columns - 1; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Environment; final class OperatingSystem { /** * Returns PHP_OS_FAMILY (if defined (which it is on PHP >= 7.2)). * Returns a string (compatible with PHP_OS_FAMILY) derived from PHP_OS otherwise. */ public function getFamily(): string { if (\defined('PHP_OS_FAMILY')) { return \PHP_OS_FAMILY; } if (\DIRECTORY_SEPARATOR === '\\') { return 'Windows'; } switch (\PHP_OS) { case 'Darwin': return 'Darwin'; case 'DragonFly': case 'FreeBSD': case 'NetBSD': case 'OpenBSD': return 'BSD'; case 'Linux': return 'Linux'; case 'SunOS': return 'Solaris'; default: return 'Unknown'; } } } sebastian/environment Copyright (c) 2014-2019, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Environment; /** * Utility class for HHVM/PHP environment handling. */ final class Runtime { /** * @var string */ private static $binary; /** * Returns true when Xdebug or PCOV is available or * the runtime used is PHPDBG. */ public function canCollectCodeCoverage(): bool { return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); } /** * Returns true when Zend OPcache is loaded, enabled, and is configured to discard comments. */ public function discardsComments(): bool { if (!\extension_loaded('Zend OPcache')) { return false; } if (\ini_get('opcache.save_comments') !== '0') { return false; } if (\PHP_SAPI === 'cli' && \ini_get('opcache.enable_cli') === '1') { return true; } if (\PHP_SAPI !== 'cli' && \ini_get('opcache.enable') === '1') { return true; } return false; } /** * Returns the path to the binary of the current runtime. * Appends ' --php' to the path when the runtime is HHVM. */ public function getBinary(): string { // HHVM if (self::$binary === null && $this->isHHVM()) { // @codeCoverageIgnoreStart if ((self::$binary = \getenv('PHP_BINARY')) === false) { self::$binary = \PHP_BINARY; } self::$binary = \escapeshellarg(self::$binary) . ' --php' . ' -d hhvm.php7.all=1'; // @codeCoverageIgnoreEnd } if (self::$binary === null && \PHP_BINARY !== '') { self::$binary = \escapeshellarg(\PHP_BINARY); } if (self::$binary === null) { // @codeCoverageIgnoreStart $possibleBinaryLocations = [ \PHP_BINDIR . '/php', \PHP_BINDIR . '/php-cli.exe', \PHP_BINDIR . '/php.exe', ]; foreach ($possibleBinaryLocations as $binary) { if (\is_readable($binary)) { self::$binary = \escapeshellarg($binary); break; } } // @codeCoverageIgnoreEnd } if (self::$binary === null) { // @codeCoverageIgnoreStart self::$binary = 'php'; // @codeCoverageIgnoreEnd } return self::$binary; } public function getNameWithVersion(): string { return $this->getName() . ' ' . $this->getVersion(); } public function getNameWithVersionAndCodeCoverageDriver(): string { if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { return $this->getNameWithVersion(); } if ($this->hasXdebug()) { return \sprintf( '%s with Xdebug %s', $this->getNameWithVersion(), \phpversion('xdebug') ); } if ($this->hasPCOV()) { return \sprintf( '%s with PCOV %s', $this->getNameWithVersion(), \phpversion('pcov') ); } } public function getName(): string { if ($this->isHHVM()) { // @codeCoverageIgnoreStart return 'HHVM'; // @codeCoverageIgnoreEnd } if ($this->isPHPDBG()) { // @codeCoverageIgnoreStart return 'PHPDBG'; // @codeCoverageIgnoreEnd } return 'PHP'; } public function getVendorUrl(): string { if ($this->isHHVM()) { // @codeCoverageIgnoreStart return 'http://hhvm.com/'; // @codeCoverageIgnoreEnd } return 'https://secure.php.net/'; } public function getVersion(): string { if ($this->isHHVM()) { // @codeCoverageIgnoreStart return HHVM_VERSION; // @codeCoverageIgnoreEnd } return \PHP_VERSION; } /** * Returns true when the runtime used is PHP and Xdebug is loaded. */ public function hasXdebug(): bool { return ($this->isPHP() || $this->isHHVM()) && \extension_loaded('xdebug'); } /** * Returns true when the runtime used is HHVM. */ public function isHHVM(): bool { return \defined('HHVM_VERSION'); } /** * Returns true when the runtime used is PHP without the PHPDBG SAPI. */ public function isPHP(): bool { return !$this->isHHVM() && !$this->isPHPDBG(); } /** * Returns true when the runtime used is PHP with the PHPDBG SAPI. */ public function isPHPDBG(): bool { return \PHP_SAPI === 'phpdbg' && !$this->isHHVM(); } /** * Returns true when the runtime used is PHP with the PHPDBG SAPI * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). */ public function hasPHPDBGCodeCoverage(): bool { return $this->isPHPDBG(); } /** * Returns true when the runtime used is PHP with PCOV loaded and enabled */ public function hasPCOV(): bool { return $this->isPHP() && \extension_loaded('pcov') && \ini_get('pcov.enabled'); } /** * Parses the loaded php.ini file (if any) as well as all * additional php.ini files from the additional ini dir for * a list of all configuration settings loaded from files * at startup. Then checks for each php.ini setting passed * via the `$values` parameter whether this setting has * been changed at runtime. Returns an array of strings * where each string has the format `key=value` denoting * the name of a changed php.ini setting with its new value. * * @return string[] */ public function getCurrentSettings(array $values): array { $diff = []; $files = []; if ($file = \php_ini_loaded_file()) { $files[] = $file; } if ($scanned = \php_ini_scanned_files()) { $files = \array_merge( $files, \array_map( 'trim', \explode(",\n", $scanned) ) ); } foreach ($files as $ini) { $config = \parse_ini_file($ini, true); foreach ($values as $value) { $set = \ini_get($value); if (isset($config[$value]) && $set != $config[$value]) { $diff[] = \sprintf('%s=%s', $value, $set); } } } return $diff; } } phpunit/phpunit: 8.1.5 doctrine/instantiator: 1.2.0 myclabs/deep-copy: 1.9.1 phar-io/manifest: 1.0.3 phar-io/version: 2.0.1 phpdocumentor/reflection-common: 1.0.1 phpdocumentor/reflection-docblock: 4.3.1 phpdocumentor/type-resolver: 0.4.0 phpspec/prophecy: 1.8.0 phpunit/php-code-coverage: 7.0.3 phpunit/php-file-iterator: 2.0.2 phpunit/php-invoker: 2.0.0 phpunit/php-text-template: 1.2.1 phpunit/php-timer: 2.1.1 phpunit/php-token-stream: 3.0.1 sebastian/code-unit-reverse-lookup: 1.0.1 sebastian/comparator: 3.0.2 sebastian/diff: 3.0.2 sebastian/environment: 4.2.2 sebastian/exporter: 3.1.0 sebastian/global-state: 3.0.0 sebastian/object-enumerator: 3.0.3 sebastian/object-reflector: 1.1.1 sebastian/recursion-context: 3.0.0 sebastian/resource-operations: 2.0.1 sebastian/version: 2.0.1 symfony/polyfill-ctype: v1.11.0 theseer/tokenizer: 1.1.2 webmozart/assert: 1.4.0 Object Reflector Copyright (c) 2017, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; use phpDocumentor\Reflection\Types\Array_; use phpDocumentor\Reflection\Types\Compound; use phpDocumentor\Reflection\Types\Context; use phpDocumentor\Reflection\Types\Iterable_; use phpDocumentor\Reflection\Types\Nullable; use phpDocumentor\Reflection\Types\Object_; final class TypeResolver { /** @var string Definition of the ARRAY operator for types */ const OPERATOR_ARRAY = '[]'; /** @var string Definition of the NAMESPACE operator in PHP */ const OPERATOR_NAMESPACE = '\\'; /** @var string[] List of recognized keywords and unto which Value Object they map */ private $keywords = array( 'string' => Types\String_::class, 'int' => Types\Integer::class, 'integer' => Types\Integer::class, 'bool' => Types\Boolean::class, 'boolean' => Types\Boolean::class, 'float' => Types\Float_::class, 'double' => Types\Float_::class, 'object' => Object_::class, 'mixed' => Types\Mixed_::class, 'array' => Array_::class, 'resource' => Types\Resource_::class, 'void' => Types\Void_::class, 'null' => Types\Null_::class, 'scalar' => Types\Scalar::class, 'callback' => Types\Callable_::class, 'callable' => Types\Callable_::class, 'false' => Types\Boolean::class, 'true' => Types\Boolean::class, 'self' => Types\Self_::class, '$this' => Types\This::class, 'static' => Types\Static_::class, 'parent' => Types\Parent_::class, 'iterable' => Iterable_::class, ); /** @var FqsenResolver */ private $fqsenResolver; /** * Initializes this TypeResolver with the means to create and resolve Fqsen objects. * * @param FqsenResolver $fqsenResolver */ public function __construct(FqsenResolver $fqsenResolver = null) { $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); } /** * Analyzes the given type and returns the FQCN variant. * * When a type is provided this method checks whether it is not a keyword or * Fully Qualified Class Name. If so it will use the given namespace and * aliases to expand the type to a FQCN representation. * * This method only works as expected if the namespace and aliases are set; * no dynamic reflection is being performed here. * * @param string $type The relative or absolute type. * @param Context $context * * @uses Context::getNamespace() to determine with what to prefix the type name. * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be * replaced with another namespace. * * @return Type|null */ public function resolve($type, Context $context = null) { if (!is_string($type)) { throw new \InvalidArgumentException( 'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true) ); } $type = trim($type); if (!$type) { throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); } if ($context === null) { $context = new Context(''); } switch (true) { case $this->isNullableType($type): return $this->resolveNullableType($type, $context); case $this->isKeyword($type): return $this->resolveKeyword($type); case ($this->isCompoundType($type)): return $this->resolveCompoundType($type, $context); case $this->isTypedArray($type): return $this->resolveTypedArray($type, $context); case $this->isFqsen($type): return $this->resolveTypedObject($type); case $this->isPartialStructuralElementName($type): return $this->resolveTypedObject($type, $context); // @codeCoverageIgnoreStart default: // I haven't got the foggiest how the logic would come here but added this as a defense. throw new \RuntimeException( 'Unable to resolve type "' . $type . '", there is no known method to resolve it' ); } // @codeCoverageIgnoreEnd } /** * Adds a keyword to the list of Keywords and associates it with a specific Value Object. * * @param string $keyword * @param string $typeClassName * * @return void */ public function addKeyword($keyword, $typeClassName) { if (!class_exists($typeClassName)) { throw new \InvalidArgumentException( 'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' . ' but we could not find the class ' . $typeClassName ); } if (!in_array(Type::class, class_implements($typeClassName))) { throw new \InvalidArgumentException( 'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"' ); } $this->keywords[$keyword] = $typeClassName; } /** * Detects whether the given type represents an array. * * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. * * @return bool */ private function isTypedArray($type) { return substr($type, -2) === self::OPERATOR_ARRAY; } /** * Detects whether the given type represents a PHPDoc keyword. * * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. * * @return bool */ private function isKeyword($type) { return in_array(strtolower($type), array_keys($this->keywords), true); } /** * Detects whether the given type represents a relative structural element name. * * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. * * @return bool */ private function isPartialStructuralElementName($type) { return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type); } /** * Tests whether the given type is a Fully Qualified Structural Element Name. * * @param string $type * * @return bool */ private function isFqsen($type) { return strpos($type, self::OPERATOR_NAMESPACE) === 0; } /** * Tests whether the given type is a compound type (i.e. `string|int`). * * @param string $type * * @return bool */ private function isCompoundType($type) { return strpos($type, '|') !== false; } /** * Test whether the given type is a nullable type (i.e. `?string`) * * @param string $type * * @return bool */ private function isNullableType($type) { return $type[0] === '?'; } /** * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set. * * @param string $type * @param Context $context * * @return Array_ */ private function resolveTypedArray($type, Context $context) { return new Array_($this->resolve(substr($type, 0, -2), $context)); } /** * Resolves the given keyword (such as `string`) into a Type object representing that keyword. * * @param string $type * * @return Type */ private function resolveKeyword($type) { $className = $this->keywords[strtolower($type)]; return new $className(); } /** * Resolves the given FQSEN string into an FQSEN object. * * @param string $type * @param Context|null $context * * @return Object_ */ private function resolveTypedObject($type, Context $context = null) { return new Object_($this->fqsenResolver->resolve($type, $context)); } /** * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN. * * @param string $type * @param Context $context * * @return Compound */ private function resolveCompoundType($type, Context $context) { $types = []; foreach (explode('|', $type) as $part) { $types[] = $this->resolve($part, $context); } return new Compound($types); } /** * Resolve nullable types (i.e. `?string`) into a Nullable type wrapper * * @param string $type * @param Context $context * * @return Nullable */ private function resolveNullableType($type, Context $context) { return new Nullable($this->resolve(ltrim($type, '?'), $context)); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; interface Type { public function __toString(); } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; use phpDocumentor\Reflection\Types\Context; class FqsenResolver { /** @var string Definition of the NAMESPACE operator in PHP */ const OPERATOR_NAMESPACE = '\\'; public function resolve($fqsen, Context $context = null) { if ($context === null) { $context = new Context(''); } if ($this->isFqsen($fqsen)) { return new Fqsen($fqsen); } return $this->resolvePartialStructuralElementName($fqsen, $context); } /** * Tests whether the given type is a Fully Qualified Structural Element Name. * * @param string $type * * @return bool */ private function isFqsen($type) { return strpos($type, self::OPERATOR_NAMESPACE) === 0; } /** * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. * * @param string $type * @param Context $context * * @return Fqsen * @throws \InvalidArgumentException when type is not a valid FQSEN. */ private function resolvePartialStructuralElementName($type, Context $context) { $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); $namespaceAliases = $context->getNamespaceAliases(); // if the first segment is not an alias; prepend namespace name and return if (!isset($namespaceAliases[$typeParts[0]])) { $namespace = $context->getNamespace(); if ('' !== $namespace) { $namespace .= self::OPERATOR_NAMESPACE; } return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); } $typeParts[0] = $namespaceAliases[$typeParts[0]]; return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts)); } } The MIT License (MIT) Copyright (c) 2010 Mike van Riel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the '$this' pseudo-type. * * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is * commonly used when documenting fluent interfaces since it represents that the same object is returned. */ final class This implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return '$this'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the pseudo-type 'void'. * * Void is generally only used when working with return types as it signifies that the method intentionally does not * return any value. */ final class Void_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'void'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the type 'string'. */ final class String_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'string'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. */ final class Scalar implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'scalar'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing a Callable type. */ final class Callable_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'callable'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; final class Integer implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'int'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing a Boolean type. */ final class Boolean implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'bool'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Represents an array type as described in the PSR-5, the PHPDoc Standard. * * An array can be represented in two forms: * * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'. * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a * type name. */ final class Array_ implements Type { /** @var Type */ private $valueType; /** @var Type */ private $keyType; /** * Initializes this representation of an array with the given Type or Fqsen. * * @param Type $valueType * @param Type $keyType */ public function __construct(Type $valueType = null, Type $keyType = null) { if ($keyType === null) { $keyType = new Compound([ new String_(), new Integer() ]); } if ($valueType === null) { $valueType = new Mixed_(); } $this->valueType = $valueType; $this->keyType = $keyType; } /** * Returns the type for the keys of this array. * * @return Type */ public function getKeyType() { return $this->keyType; } /** * Returns the value for the keys of this array. * * @return Type */ public function getValueType() { return $this->valueType; } /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { if ($this->valueType instanceof Mixed_) { return 'array'; } return $this->valueType . '[]'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing a null value or type. */ final class Null_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'null'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\Type; /** * Value Object representing an object. * * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN, * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects * in general. */ final class Object_ implements Type { /** @var Fqsen|null */ private $fqsen; /** * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'. * * @param Fqsen $fqsen * @throws \InvalidArgumentException when provided $fqsen is not a valid type. */ public function __construct(Fqsen $fqsen = null) { if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) { throw new \InvalidArgumentException( 'Object types can only refer to a class, interface or trait but a method, function, constant or ' . 'property was received: ' . (string)$fqsen ); } $this->fqsen = $fqsen; } /** * Returns the FQSEN associated with this object. * * @return Fqsen|null */ public function getFqsen() { return $this->fqsen; } /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { if ($this->fqsen) { return (string)$this->fqsen; } return 'object'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing a Float. */ final class Float_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'float'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the 'resource' Type. */ final class Resource_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'resource'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the 'static' type. * * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does * not take inheritance into account but static means that the return type is always that of the class of the called * element. * * See the documentation on late static binding in the PHP Documentation for more information on the difference between * static and self. */ final class Static_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'static'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing a nullable type. The real type is wrapped. */ final class Nullable implements Type { /** * @var Type */ private $realType; /** * Initialises this nullable type using the real type embedded * * @param Type $realType */ public function __construct(Type $realType) { $this->realType = $realType; } /** * Provide access to the actual type directly, if needed. * * @return Type */ public function getActualType() { return $this->realType; } /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return '?' . $this->realType->__toString(); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the 'self' type. * * Self, as a Type, represents the class in which the associated element was defined. */ final class Self_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'self'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing iterable type */ final class Iterable_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'iterable'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing the 'parent' type. * * Parent, as a Type, represents the parent class of class in which the associated element was defined. */ final class Parent_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'parent'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; /** * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor. * * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to * Fully Qualified names. * * @see Context for more information. */ final class ContextFactory { /** The literal used at the end of a use statement. */ const T_LITERAL_END_OF_USE = ';'; /** The literal used between sets of use statements */ const T_LITERAL_USE_SEPARATOR = ','; /** * Build a Context given a Class Reflection. * * @param \Reflector $reflector * * @see Context for more information on Contexts. * * @return Context */ public function createFromReflector(\Reflector $reflector) { if (method_exists($reflector, 'getDeclaringClass')) { $reflector = $reflector->getDeclaringClass(); } $fileName = $reflector->getFileName(); $namespace = $reflector->getNamespaceName(); if (file_exists($fileName)) { return $this->createForNamespace($namespace, file_get_contents($fileName)); } return new Context($namespace, []); } /** * Build a Context for a namespace in the provided file contents. * * @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes. * @param string $fileContents the file's contents to retrieve the aliases from with the given namespace. * * @see Context for more information on Contexts. * * @return Context */ public function createForNamespace($namespace, $fileContents) { $namespace = trim($namespace, '\\'); $useStatements = []; $currentNamespace = ''; $tokens = new \ArrayIterator(token_get_all($fileContents)); while ($tokens->valid()) { switch ($tokens->current()[0]) { case T_NAMESPACE: $currentNamespace = $this->parseNamespace($tokens); break; case T_CLASS: // Fast-forward the iterator through the class so that any // T_USE tokens found within are skipped - these are not // valid namespace use statements so should be ignored. $braceLevel = 0; $firstBraceFound = false; while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { if ($tokens->current() === '{' || $tokens->current()[0] === T_CURLY_OPEN || $tokens->current()[0] === T_DOLLAR_OPEN_CURLY_BRACES) { if (!$firstBraceFound) { $firstBraceFound = true; } $braceLevel++; } if ($tokens->current() === '}') { $braceLevel--; } $tokens->next(); } break; case T_USE: if ($currentNamespace === $namespace) { $useStatements = array_merge($useStatements, $this->parseUseStatement($tokens)); } break; } $tokens->next(); } return new Context($namespace, $useStatements); } /** * Deduce the name from tokens when we are at the T_NAMESPACE token. * * @param \ArrayIterator $tokens * * @return string */ private function parseNamespace(\ArrayIterator $tokens) { // skip to the first string or namespace separator $this->skipToNextStringOrNamespaceSeparator($tokens); $name = ''; while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) ) { $name .= $tokens->current()[1]; $tokens->next(); } return $name; } /** * Deduce the names of all imports when we are at the T_USE token. * * @param \ArrayIterator $tokens * * @return string[] */ private function parseUseStatement(\ArrayIterator $tokens) { $uses = []; $continue = true; while ($continue) { $this->skipToNextStringOrNamespaceSeparator($tokens); list($alias, $fqnn) = $this->extractUseStatement($tokens); $uses[$alias] = $fqnn; if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) { $continue = false; } } return $uses; } /** * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. * * @param \ArrayIterator $tokens * * @return void */ private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens) { while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) { $tokens->next(); } } /** * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of * a USE statement yet. * * @param \ArrayIterator $tokens * * @return string */ private function extractUseStatement(\ArrayIterator $tokens) { $result = ['']; while ($tokens->valid() && ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR) && ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE) ) { if ($tokens->current()[0] === T_AS) { $result[] = ''; } if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) { $result[count($result) - 1] .= $tokens->current()[1]; } $tokens->next(); } if (count($result) == 1) { $backslashPos = strrpos($result[0], '\\'); if (false !== $backslashPos) { $result[] = substr($result[0], $backslashPos + 1); } else { $result[] = $result[0]; } } return array_reverse($result); } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Type; /** * Value Object representing an unknown, or mixed, type. */ final class Mixed_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return 'mixed'; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; /** * Provides information about the Context in which the DocBlock occurs that receives this context. * * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since * you can provide a short form or make use of namespace aliases. * * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in * which an associated class resides for its namespace and imports. * * @see ContextFactory::createFromClassReflector() * @see ContextFactory::createForNamespace() */ final class Context { /** @var string The current namespace. */ private $namespace; /** @var array List of namespace aliases => Fully Qualified Namespace. */ private $namespaceAliases; /** * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) * format (without a preceding `\`). * * @param string $namespace The namespace where this DocBlock resides in. * @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace. */ public function __construct($namespace, array $namespaceAliases = []) { $this->namespace = ('global' !== $namespace && 'default' !== $namespace) ? trim((string)$namespace, '\\') : ''; foreach ($namespaceAliases as $alias => $fqnn) { if ($fqnn[0] === '\\') { $fqnn = substr($fqnn, 1); } if ($fqnn[strlen($fqnn) - 1] === '\\') { $fqnn = substr($fqnn, 0, -1); } $namespaceAliases[$alias] = $fqnn; } $this->namespaceAliases = $namespaceAliases; } /** * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. * * @return string */ public function getNamespace() { return $this->namespace; } /** * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent * the alias for the imported Namespace. * * @return string[] */ public function getNamespaceAliases() { return $this->namespaceAliases; } } * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\Types; use ArrayIterator; use IteratorAggregate; use phpDocumentor\Reflection\Type; /** * Value Object representing a Compound Type. * * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type * may contain a value with any of the given types. */ final class Compound implements Type, IteratorAggregate { /** @var Type[] */ private $types; /** * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface. * * @param Type[] $types * @throws \InvalidArgumentException when types are not all instance of Type */ public function __construct(array $types) { foreach ($types as $type) { if (!$type instanceof Type) { throw new \InvalidArgumentException('A compound type can only have other types as elements'); } } $this->types = $types; } /** * Returns the type at the given index. * * @param integer $index * * @return Type|null */ public function get($index) { if (!$this->has($index)) { return null; } return $this->types[$index]; } /** * Tests if this compound type has a type with the given index. * * @param integer $index * * @return bool */ public function has($index) { return isset($this->types[$index]); } /** * Returns a rendered output of the Type as it would be used in a DocBlock. * * @return string */ public function __toString() { return implode('|', $this->types); } /** * {@inheritdoc} */ public function getIterator() { return new ArrayIterator($this->types); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann; /** * @since Class available since Release 1.0.0 */ class Version { /** * @var string */ private $path; /** * @var string */ private $release; /** * @var string */ private $version; /** * @param string $release * @param string $path */ public function __construct($release, $path) { $this->release = $release; $this->path = $path; } /** * @return string */ public function getVersion() { if ($this->version === null) { if (count(explode('.', $this->release)) == 3) { $this->version = $this->release; } else { $this->version = $this->release . '-dev'; } $git = $this->getGitInformation($this->path); if ($git) { if (count(explode('.', $this->release)) == 3) { $this->version = $git; } else { $git = explode('-', $git); $this->version = $this->release . '-' . end($git); } } } return $this->version; } /** * @param string $path * * @return bool|string */ private function getGitInformation($path) { if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) { return false; } $process = proc_open( 'git describe --tags', [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes, $path ); if (!is_resource($process)) { return false; } $result = trim(stream_get_contents($pipes[1])); fclose($pipes[1]); fclose($pipes[2]); $returnCode = proc_close($process); if ($returnCode !== 0) { return false; } return $result; } } Version Copyright (c) 2013-2015, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Webmozart\Assert; use ArrayAccess; use BadMethodCallException; use Closure; use Countable; use Exception; use InvalidArgumentException; use Throwable; use Traversable; /** * Efficient assertions to validate the input/output of your methods. * * @method static void nullOrString($value, $message = '') * @method static void nullOrStringNotEmpty($value, $message = '') * @method static void nullOrInteger($value, $message = '') * @method static void nullOrIntegerish($value, $message = '') * @method static void nullOrFloat($value, $message = '') * @method static void nullOrNumeric($value, $message = '') * @method static void nullOrNatural($value, $message = '') * @method static void nullOrBoolean($value, $message = '') * @method static void nullOrScalar($value, $message = '') * @method static void nullOrObject($value, $message = '') * @method static void nullOrResource($value, $type = null, $message = '') * @method static void nullOrIsCallable($value, $message = '') * @method static void nullOrIsArray($value, $message = '') * @method static void nullOrIsTraversable($value, $message = '') * @method static void nullOrIsArrayAccessible($value, $message = '') * @method static void nullOrIsCountable($value, $message = '') * @method static void nullOrIsIterable($value, $message = '') * @method static void nullOrIsInstanceOf($value, $class, $message = '') * @method static void nullOrNotInstanceOf($value, $class, $message = '') * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '') * @method static void nullOrIsEmpty($value, $message = '') * @method static void nullOrNotEmpty($value, $message = '') * @method static void nullOrTrue($value, $message = '') * @method static void nullOrFalse($value, $message = '') * @method static void nullOrIp($value, $message = '') * @method static void nullOrIpv4($value, $message = '') * @method static void nullOrIpv6($value, $message = '') * @method static void nullOrEq($value, $value2, $message = '') * @method static void nullOrNotEq($value,$value2, $message = '') * @method static void nullOrSame($value, $value2, $message = '') * @method static void nullOrNotSame($value, $value2, $message = '') * @method static void nullOrGreaterThan($value, $value2, $message = '') * @method static void nullOrGreaterThanEq($value, $value2, $message = '') * @method static void nullOrLessThan($value, $value2, $message = '') * @method static void nullOrLessThanEq($value, $value2, $message = '') * @method static void nullOrRange($value, $min, $max, $message = '') * @method static void nullOrOneOf($value, $values, $message = '') * @method static void nullOrContains($value, $subString, $message = '') * @method static void nullOrNotContains($value, $subString, $message = '') * @method static void nullOrNotWhitespaceOnly($value, $message = '') * @method static void nullOrStartsWith($value, $prefix, $message = '') * @method static void nullOrStartsWithLetter($value, $message = '') * @method static void nullOrEndsWith($value, $suffix, $message = '') * @method static void nullOrRegex($value, $pattern, $message = '') * @method static void nullOrNotRegex($value, $pattern, $message = '') * @method static void nullOrAlpha($value, $message = '') * @method static void nullOrDigits($value, $message = '') * @method static void nullOrAlnum($value, $message = '') * @method static void nullOrLower($value, $message = '') * @method static void nullOrUpper($value, $message = '') * @method static void nullOrLength($value, $length, $message = '') * @method static void nullOrMinLength($value, $min, $message = '') * @method static void nullOrMaxLength($value, $max, $message = '') * @method static void nullOrLengthBetween($value, $min, $max, $message = '') * @method static void nullOrFileExists($value, $message = '') * @method static void nullOrFile($value, $message = '') * @method static void nullOrDirectory($value, $message = '') * @method static void nullOrReadable($value, $message = '') * @method static void nullOrWritable($value, $message = '') * @method static void nullOrClassExists($value, $message = '') * @method static void nullOrSubclassOf($value, $class, $message = '') * @method static void nullOrInterfaceExists($value, $message = '') * @method static void nullOrImplementsInterface($value, $interface, $message = '') * @method static void nullOrPropertyExists($value, $property, $message = '') * @method static void nullOrPropertyNotExists($value, $property, $message = '') * @method static void nullOrMethodExists($value, $method, $message = '') * @method static void nullOrMethodNotExists($value, $method, $message = '') * @method static void nullOrKeyExists($value, $key, $message = '') * @method static void nullOrKeyNotExists($value, $key, $message = '') * @method static void nullOrCount($value, $key, $message = '') * @method static void nullOrMinCount($value, $min, $message = '') * @method static void nullOrMaxCount($value, $max, $message = '') * @method static void nullOrIsList($value, $message = '') * @method static void nullOrIsMap($value, $message = '') * @method static void nullOrCountBetween($value, $min, $max, $message = '') * @method static void nullOrUuid($values, $message = '') * @method static void nullOrThrows($expression, $class = 'Exception', $message = '') * @method static void allString($values, $message = '') * @method static void allStringNotEmpty($values, $message = '') * @method static void allInteger($values, $message = '') * @method static void allIntegerish($values, $message = '') * @method static void allFloat($values, $message = '') * @method static void allNumeric($values, $message = '') * @method static void allNatural($values, $message = '') * @method static void allBoolean($values, $message = '') * @method static void allScalar($values, $message = '') * @method static void allObject($values, $message = '') * @method static void allResource($values, $type = null, $message = '') * @method static void allIsCallable($values, $message = '') * @method static void allIsArray($values, $message = '') * @method static void allIsTraversable($values, $message = '') * @method static void allIsArrayAccessible($values, $message = '') * @method static void allIsCountable($values, $message = '') * @method static void allIsIterable($values, $message = '') * @method static void allIsInstanceOf($values, $class, $message = '') * @method static void allNotInstanceOf($values, $class, $message = '') * @method static void allIsInstanceOfAny($values, $classes, $message = '') * @method static void allNull($values, $message = '') * @method static void allNotNull($values, $message = '') * @method static void allIsEmpty($values, $message = '') * @method static void allNotEmpty($values, $message = '') * @method static void allTrue($values, $message = '') * @method static void allFalse($values, $message = '') * @method static void allIp($values, $message = '') * @method static void allIpv4($values, $message = '') * @method static void allIpv6($values, $message = '') * @method static void allEq($values, $value2, $message = '') * @method static void allNotEq($values,$value2, $message = '') * @method static void allSame($values, $value2, $message = '') * @method static void allNotSame($values, $value2, $message = '') * @method static void allGreaterThan($values, $value2, $message = '') * @method static void allGreaterThanEq($values, $value2, $message = '') * @method static void allLessThan($values, $value2, $message = '') * @method static void allLessThanEq($values, $value2, $message = '') * @method static void allRange($values, $min, $max, $message = '') * @method static void allOneOf($values, $values, $message = '') * @method static void allContains($values, $subString, $message = '') * @method static void allNotContains($values, $subString, $message = '') * @method static void allNotWhitespaceOnly($values, $message = '') * @method static void allStartsWith($values, $prefix, $message = '') * @method static void allStartsWithLetter($values, $message = '') * @method static void allEndsWith($values, $suffix, $message = '') * @method static void allRegex($values, $pattern, $message = '') * @method static void allNotRegex($values, $pattern, $message = '') * @method static void allAlpha($values, $message = '') * @method static void allDigits($values, $message = '') * @method static void allAlnum($values, $message = '') * @method static void allLower($values, $message = '') * @method static void allUpper($values, $message = '') * @method static void allLength($values, $length, $message = '') * @method static void allMinLength($values, $min, $message = '') * @method static void allMaxLength($values, $max, $message = '') * @method static void allLengthBetween($values, $min, $max, $message = '') * @method static void allFileExists($values, $message = '') * @method static void allFile($values, $message = '') * @method static void allDirectory($values, $message = '') * @method static void allReadable($values, $message = '') * @method static void allWritable($values, $message = '') * @method static void allClassExists($values, $message = '') * @method static void allSubclassOf($values, $class, $message = '') * @method static void allInterfaceExists($values, $message = '') * @method static void allImplementsInterface($values, $interface, $message = '') * @method static void allPropertyExists($values, $property, $message = '') * @method static void allPropertyNotExists($values, $property, $message = '') * @method static void allMethodExists($values, $method, $message = '') * @method static void allMethodNotExists($values, $method, $message = '') * @method static void allKeyExists($values, $key, $message = '') * @method static void allKeyNotExists($values, $key, $message = '') * @method static void allCount($values, $key, $message = '') * @method static void allMinCount($values, $min, $message = '') * @method static void allMaxCount($values, $max, $message = '') * @method static void allCountBetween($values, $min, $max, $message = '') * @method static void allIsList($values, $message = '') * @method static void allIsMap($values, $message = '') * @method static void allUuid($values, $message = '') * @method static void allThrows($expressions, $class = 'Exception', $message = '') * * @since 1.0 * * @author Bernhard Schussek */ class Assert { public static function string($value, $message = '') { if (!is_string($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a string. Got: %s', static::typeToString($value) )); } } public static function stringNotEmpty($value, $message = '') { static::string($value, $message); static::notEq($value, '', $message); } public static function integer($value, $message = '') { if (!is_int($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an integer. Got: %s', static::typeToString($value) )); } } public static function integerish($value, $message = '') { if (!is_numeric($value) || $value != (int) $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an integerish value. Got: %s', static::typeToString($value) )); } } public static function float($value, $message = '') { if (!is_float($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a float. Got: %s', static::typeToString($value) )); } } public static function numeric($value, $message = '') { if (!is_numeric($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a numeric. Got: %s', static::typeToString($value) )); } } public static function natural($value, $message = '') { if (!is_int($value) || $value < 0) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a non-negative integer. Got %s', static::valueToString($value) )); } } public static function boolean($value, $message = '') { if (!is_bool($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a boolean. Got: %s', static::typeToString($value) )); } } public static function scalar($value, $message = '') { if (!is_scalar($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a scalar. Got: %s', static::typeToString($value) )); } } public static function object($value, $message = '') { if (!is_object($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an object. Got: %s', static::typeToString($value) )); } } public static function resource($value, $type = null, $message = '') { if (!is_resource($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a resource. Got: %s', static::typeToString($value) )); } if ($type && $type !== get_resource_type($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a resource of type %2$s. Got: %s', static::typeToString($value), $type )); } } public static function isCallable($value, $message = '') { if (!is_callable($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a callable. Got: %s', static::typeToString($value) )); } } public static function isArray($value, $message = '') { if (!is_array($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an array. Got: %s', static::typeToString($value) )); } } public static function isTraversable($value, $message = '') { @trigger_error( sprintf( 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', __METHOD__ ), E_USER_DEPRECATED ); if (!is_array($value) && !($value instanceof Traversable)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a traversable. Got: %s', static::typeToString($value) )); } } public static function isArrayAccessible($value, $message = '') { if (!is_array($value) && !($value instanceof ArrayAccess)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an array accessible. Got: %s', static::typeToString($value) )); } } public static function isCountable($value, $message = '') { if (!is_array($value) && !($value instanceof Countable)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a countable. Got: %s', static::typeToString($value) )); } } public static function isIterable($value, $message = '') { if (!is_array($value) && !($value instanceof Traversable)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an iterable. Got: %s', static::typeToString($value) )); } } public static function isInstanceOf($value, $class, $message = '') { if (!($value instanceof $class)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an instance of %2$s. Got: %s', static::typeToString($value), $class )); } } public static function notInstanceOf($value, $class, $message = '') { if ($value instanceof $class) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an instance other than %2$s. Got: %s', static::typeToString($value), $class )); } } public static function isInstanceOfAny($value, array $classes, $message = '') { foreach ($classes as $class) { if ($value instanceof $class) { return; } } static::reportInvalidArgument(sprintf( $message ?: 'Expected an instance of any of %2$s. Got: %s', static::typeToString($value), implode(', ', array_map(array('static', 'valueToString'), $classes)) )); } public static function isEmpty($value, $message = '') { if (!empty($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an empty value. Got: %s', static::valueToString($value) )); } } public static function notEmpty($value, $message = '') { if (empty($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a non-empty value. Got: %s', static::valueToString($value) )); } } public static function null($value, $message = '') { if (null !== $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected null. Got: %s', static::valueToString($value) )); } } public static function notNull($value, $message = '') { if (null === $value) { static::reportInvalidArgument( $message ?: 'Expected a value other than null.' ); } } public static function true($value, $message = '') { if (true !== $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to be true. Got: %s', static::valueToString($value) )); } } public static function false($value, $message = '') { if (false !== $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to be false. Got: %s', static::valueToString($value) )); } } public static function ip($value, $message = '') { if (false === filter_var($value, FILTER_VALIDATE_IP)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to be an IP. Got: %s', static::valueToString($value) )); } } public static function ipv4($value, $message = '') { if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to be an IPv4. Got: %s', static::valueToString($value) )); } } public static function ipv6($value, $message = '') { if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to be an IPv6. Got %s', static::valueToString($value) )); } } public static function eq($value, $value2, $message = '') { if ($value2 != $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($value2) )); } } public static function notEq($value, $value2, $message = '') { if ($value2 == $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a different value than %s.', static::valueToString($value2) )); } } public static function same($value, $value2, $message = '') { if ($value2 !== $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value identical to %2$s. Got: %s', static::valueToString($value), static::valueToString($value2) )); } } public static function notSame($value, $value2, $message = '') { if ($value2 === $value) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value not identical to %s.', static::valueToString($value2) )); } } public static function greaterThan($value, $limit, $message = '') { if ($value <= $limit) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value greater than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit) )); } } public static function greaterThanEq($value, $limit, $message = '') { if ($value < $limit) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value greater than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit) )); } } public static function lessThan($value, $limit, $message = '') { if ($value >= $limit) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value less than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit) )); } } public static function lessThanEq($value, $limit, $message = '') { if ($value > $limit) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value less than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit) )); } } public static function range($value, $min, $max, $message = '') { if ($value < $min || $value > $max) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value between %2$s and %3$s. Got: %s', static::valueToString($value), static::valueToString($min), static::valueToString($max) )); } } public static function oneOf($value, array $values, $message = '') { if (!in_array($value, $values, true)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected one of: %2$s. Got: %s', static::valueToString($value), implode(', ', array_map(array('static', 'valueToString'), $values)) )); } } public static function contains($value, $subString, $message = '') { if (false === strpos($value, $subString)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain %2$s. Got: %s', static::valueToString($value), static::valueToString($subString) )); } } public static function notContains($value, $subString, $message = '') { if (false !== strpos($value, $subString)) { static::reportInvalidArgument(sprintf( $message ?: '%2$s was not expected to be contained in a value. Got: %s', static::valueToString($value), static::valueToString($subString) )); } } public static function notWhitespaceOnly($value, $message = '') { if (preg_match('/^\s*$/', $value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a non-whitespace string. Got: %s', static::valueToString($value) )); } } public static function startsWith($value, $prefix, $message = '') { if (0 !== strpos($value, $prefix)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix) )); } } public static function startsWithLetter($value, $message = '') { $valid = isset($value[0]); if ($valid) { $locale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'C'); $valid = ctype_alpha($value[0]); setlocale(LC_CTYPE, $locale); } if (!$valid) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to start with a letter. Got: %s', static::valueToString($value) )); } } public static function endsWith($value, $suffix, $message = '') { if ($suffix !== substr($value, -static::strlen($suffix))) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix) )); } } public static function regex($value, $pattern, $message = '') { if (!preg_match($pattern, $value)) { static::reportInvalidArgument(sprintf( $message ?: 'The value %s does not match the expected pattern.', static::valueToString($value) )); } } public static function notRegex($value, $pattern, $message = '') { if (preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) { static::reportInvalidArgument(sprintf( $message ?: 'The value %s matches the pattern %s (at offset %d).', static::valueToString($value), static::valueToString($pattern), $matches[0][1] )); } } public static function alpha($value, $message = '') { $locale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'C'); $valid = !ctype_alpha($value); setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain only letters. Got: %s', static::valueToString($value) )); } } public static function digits($value, $message = '') { $locale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'C'); $valid = !ctype_digit($value); setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain digits only. Got: %s', static::valueToString($value) )); } } public static function alnum($value, $message = '') { $locale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'C'); $valid = !ctype_alnum($value); setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain letters and digits only. Got: %s', static::valueToString($value) )); } } public static function lower($value, $message = '') { $locale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'C'); $valid = !ctype_lower($value); setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain lowercase characters only. Got: %s', static::valueToString($value) )); } } public static function upper($value, $message = '') { $locale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'C'); $valid = !ctype_upper($value); setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain uppercase characters only. Got: %s', static::valueToString($value) )); } } public static function length($value, $length, $message = '') { if ($length !== static::strlen($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain %2$s characters. Got: %s', static::valueToString($value), $length )); } } public static function minLength($value, $min, $message = '') { if (static::strlen($value) < $min) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain at least %2$s characters. Got: %s', static::valueToString($value), $min )); } } public static function maxLength($value, $max, $message = '') { if (static::strlen($value) > $max) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain at most %2$s characters. Got: %s', static::valueToString($value), $max )); } } public static function lengthBetween($value, $min, $max, $message = '') { $length = static::strlen($value); if ($length < $min || $length > $max) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', static::valueToString($value), $min, $max )); } } public static function fileExists($value, $message = '') { static::string($value); if (!file_exists($value)) { static::reportInvalidArgument(sprintf( $message ?: 'The file %s does not exist.', static::valueToString($value) )); } } public static function file($value, $message = '') { static::fileExists($value, $message); if (!is_file($value)) { static::reportInvalidArgument(sprintf( $message ?: 'The path %s is not a file.', static::valueToString($value) )); } } public static function directory($value, $message = '') { static::fileExists($value, $message); if (!is_dir($value)) { static::reportInvalidArgument(sprintf( $message ?: 'The path %s is no directory.', static::valueToString($value) )); } } public static function readable($value, $message = '') { if (!is_readable($value)) { static::reportInvalidArgument(sprintf( $message ?: 'The path %s is not readable.', static::valueToString($value) )); } } public static function writable($value, $message = '') { if (!is_writable($value)) { static::reportInvalidArgument(sprintf( $message ?: 'The path %s is not writable.', static::valueToString($value) )); } } public static function classExists($value, $message = '') { if (!class_exists($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an existing class name. Got: %s', static::valueToString($value) )); } } public static function subclassOf($value, $class, $message = '') { if (!is_subclass_of($value, $class)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected a sub-class of %2$s. Got: %s', static::valueToString($value), static::valueToString($class) )); } } public static function interfaceExists($value, $message = '') { if (!interface_exists($value)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an existing interface name. got %s', static::valueToString($value) )); } } public static function implementsInterface($value, $interface, $message = '') { if (!in_array($interface, class_implements($value))) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an implementation of %2$s. Got: %s', static::valueToString($value), static::valueToString($interface) )); } } public static function propertyExists($classOrObject, $property, $message = '') { if (!property_exists($classOrObject, $property)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected the property %s to exist.', static::valueToString($property) )); } } public static function propertyNotExists($classOrObject, $property, $message = '') { if (property_exists($classOrObject, $property)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected the property %s to not exist.', static::valueToString($property) )); } } public static function methodExists($classOrObject, $method, $message = '') { if (!method_exists($classOrObject, $method)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected the method %s to exist.', static::valueToString($method) )); } } public static function methodNotExists($classOrObject, $method, $message = '') { if (method_exists($classOrObject, $method)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected the method %s to not exist.', static::valueToString($method) )); } } public static function keyExists($array, $key, $message = '') { if (!(isset($array[$key]) || array_key_exists($key, $array))) { static::reportInvalidArgument(sprintf( $message ?: 'Expected the key %s to exist.', static::valueToString($key) )); } } public static function keyNotExists($array, $key, $message = '') { if (isset($array[$key]) || array_key_exists($key, $array)) { static::reportInvalidArgument(sprintf( $message ?: 'Expected the key %s to not exist.', static::valueToString($key) )); } } public static function count($array, $number, $message = '') { static::eq( count($array), $number, $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array)) ); } public static function minCount($array, $min, $message = '') { if (count($array) < $min) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an array to contain at least %2$d elements. Got: %d', count($array), $min )); } } public static function maxCount($array, $max, $message = '') { if (count($array) > $max) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an array to contain at most %2$d elements. Got: %d', count($array), $max )); } } public static function countBetween($array, $min, $max, $message = '') { $count = count($array); if ($count < $min || $count > $max) { static::reportInvalidArgument(sprintf( $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', $count, $min, $max )); } } public static function isList($array, $message = '') { if (!is_array($array) || !$array || array_keys($array) !== range(0, count($array) - 1)) { static::reportInvalidArgument( $message ?: 'Expected list - non-associative array.' ); } } public static function isMap($array, $message = '') { if ( !is_array($array) || !$array || array_keys($array) !== array_filter(array_keys($array), function ($key) { return is_string($key); }) ) { static::reportInvalidArgument( $message ?: 'Expected map - associative array with string keys.' ); } } public static function uuid($value, $message = '') { $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); // The nil UUID is special form of UUID that is specified to have all // 128 bits set to zero. if ('00000000-0000-0000-0000-000000000000' === $value) { return; } if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { static::reportInvalidArgument(sprintf( $message ?: 'Value %s is not a valid UUID.', static::valueToString($value) )); } } public static function throws(Closure $expression, $class = 'Exception', $message = '') { static::string($class); $actual = 'none'; try { $expression(); } catch (Exception $e) { $actual = get_class($e); if ($e instanceof $class) { return; } } catch (Throwable $e) { $actual = get_class($e); if ($e instanceof $class) { return; } } static::reportInvalidArgument($message ?: sprintf( 'Expected to throw "%s", got "%s"', $class, $actual )); } public static function __callStatic($name, $arguments) { if ('nullOr' === substr($name, 0, 6)) { if (null !== $arguments[0]) { $method = lcfirst(substr($name, 6)); call_user_func_array(array('static', $method), $arguments); } return; } if ('all' === substr($name, 0, 3)) { static::isIterable($arguments[0]); $method = lcfirst(substr($name, 3)); $args = $arguments; foreach ($arguments[0] as $entry) { $args[0] = $entry; call_user_func_array(array('static', $method), $args); } return; } throw new BadMethodCallException('No such method: '.$name); } protected static function valueToString($value) { if (null === $value) { return 'null'; } if (true === $value) { return 'true'; } if (false === $value) { return 'false'; } if (is_array($value)) { return 'array'; } if (is_object($value)) { if (method_exists($value, '__toString')) { return get_class($value).': '.self::valueToString($value->__toString()); } return get_class($value); } if (is_resource($value)) { return 'resource'; } if (is_string($value)) { return '"'.$value.'"'; } return (string) $value; } protected static function typeToString($value) { return is_object($value) ? get_class($value) : gettype($value); } protected static function strlen($value) { if (!function_exists('mb_detect_encoding')) { return strlen($value); } if (false === $encoding = mb_detect_encoding($value)) { return strlen($value); } return mb_strwidth($value, $encoding); } protected static function reportInvalidArgument($message) { throw new InvalidArgumentException($message); } private function __construct() { } } The MIT License (MIT) Copyright (c) 2014 Bernhard Schussek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\FileIterator; class Factory { /** * @param array|string $paths * @param array|string $suffixes * @param array|string $prefixes * @param array $exclude * * @return \AppendIterator */ public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []): \AppendIterator { if (\is_string($paths)) { $paths = [$paths]; } $paths = $this->getPathsAfterResolvingWildcards($paths); $exclude = $this->getPathsAfterResolvingWildcards($exclude); if (\is_string($prefixes)) { if ($prefixes !== '') { $prefixes = [$prefixes]; } else { $prefixes = []; } } if (\is_string($suffixes)) { if ($suffixes !== '') { $suffixes = [$suffixes]; } else { $suffixes = []; } } $iterator = new \AppendIterator; foreach ($paths as $path) { if (\is_dir($path)) { $iterator->append( new Iterator( $path, new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS | \RecursiveDirectoryIterator::SKIP_DOTS) ), $suffixes, $prefixes, $exclude ) ); } } return $iterator; } protected function getPathsAfterResolvingWildcards(array $paths): array { $_paths = []; foreach ($paths as $path) { if ($locals = \glob($path, GLOB_ONLYDIR)) { $_paths = \array_merge($_paths, \array_map('\realpath', $locals)); } else { $_paths[] = \realpath($path); } } return \array_filter($_paths); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\FileIterator; class Iterator extends \FilterIterator { const PREFIX = 0; const SUFFIX = 1; /** * @var string */ private $basePath; /** * @var array */ private $suffixes = []; /** * @var array */ private $prefixes = []; /** * @var array */ private $exclude = []; /** * @param string $basePath * @param \Iterator $iterator * @param array $suffixes * @param array $prefixes * @param array $exclude */ public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = []) { $this->basePath = \realpath($basePath); $this->prefixes = $prefixes; $this->suffixes = $suffixes; $this->exclude = \array_filter(\array_map('realpath', $exclude)); parent::__construct($iterator); } public function accept() { $current = $this->getInnerIterator()->current(); $filename = $current->getFilename(); $realPath = $current->getRealPath(); return $this->acceptPath($realPath) && $this->acceptPrefix($filename) && $this->acceptSuffix($filename); } private function acceptPath(string $path): bool { // Filter files in hidden directories by checking path that is relative to the base path. if (\preg_match('=/\.[^/]*/=', \str_replace($this->basePath, '', $path))) { return false; } foreach ($this->exclude as $exclude) { if (\strpos($path, $exclude) === 0) { return false; } } return true; } private function acceptPrefix(string $filename): bool { return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); } private function acceptSuffix(string $filename): bool { return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); } private function acceptSubString(string $filename, array $subStrings, int $type): bool { if (empty($subStrings)) { return true; } $matched = false; foreach ($subStrings as $string) { if (($type === self::PREFIX && \strpos($filename, $string) === 0) || ($type === self::SUFFIX && \substr($filename, -1 * \strlen($string)) === $string)) { $matched = true; break; } } return $matched; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\FileIterator; class Facade { /** * @param array|string $paths * @param array|string $suffixes * @param array|string $prefixes * @param array $exclude * @param bool $commonPath * * @return array */ public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = false): array { if (\is_string($paths)) { $paths = [$paths]; } $factory = new Factory; $iterator = $factory->getFileIterator($paths, $suffixes, $prefixes, $exclude); $files = []; 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 [ 'commonPath' => $this->getCommonPath($files), 'files' => $files ]; } return $files; } protected function getCommonPath(array $files): string { $count = \count($files); if ($count === 0) { return ''; } if ($count === 1) { return \dirname($files[0]) . DIRECTORY_SEPARATOR; } $_files = []; 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; } } php-file-iterator Copyright (c) 2009-2018, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. tokens[] = $token; } /** * @return Token */ public function current(): Token { return current($this->tokens); } /** * @return int */ public function key(): int { return key($this->tokens); } /** * @return void */ public function next() { next($this->tokens); $this->pos++; } /** * @return bool */ public function valid(): bool { return $this->count() > $this->pos; } /** * @return void */ public function rewind() { reset($this->tokens); $this->pos = 0; } /** * @return int */ public function count(): int { return count($this->tokens); } /** * @param mixed $offset * * @return bool */ public function offsetExists($offset): bool { return isset($this->tokens[$offset]); } /** * @param mixed $offset * * @return Token * @throws TokenCollectionException */ public function offsetGet($offset): Token { if (!$this->offsetExists($offset)) { throw new TokenCollectionException( sprintf('No Token at offest %s', $offset) ); } return $this->tokens[$offset]; } /** * @param mixed $offset * @param Token $value * * @throws TokenCollectionException */ public function offsetSet($offset, $value) { if (!is_int($offset)) { $type = gettype($offset); throw new TokenCollectionException( sprintf( 'Offset must be of type integer, %s given', $type === 'object' ? get_class($value) : $type ) ); } if (!$value instanceof Token) { $type = gettype($value); throw new TokenCollectionException( sprintf( 'Value must be of type %s, %s given', Token::class, $type === 'object' ? get_class($value) : $type ) ); } $this->tokens[$offset] = $value; } /** * @param mixed $offset */ public function offsetUnset($offset) { unset($this->tokens[$offset]); } } ensureValidUri($value); $this->value = $value; } public function asString(): string { return $this->value; } private function ensureValidUri($value) { if (strpos($value, ':') === false) { throw new NamespaceUriException( sprintf("Namespace URI '%s' must contain at least one colon", $value) ); } } } xmlns = $xmlns; } /** * @param TokenCollection $tokens * * @return DOMDocument */ public function toDom(TokenCollection $tokens): DOMDocument { $dom = new DOMDocument(); $dom->preserveWhiteSpace = false; $dom->loadXML($this->toXML($tokens)); return $dom; } /** * @param TokenCollection $tokens * * @return string */ public function toXML(TokenCollection $tokens): string { $this->writer = new \XMLWriter(); $this->writer->openMemory(); $this->writer->setIndent(true); $this->writer->startDocument(); $this->writer->startElement('source'); $this->writer->writeAttribute('xmlns', $this->xmlns->asString()); $this->writer->startElement('line'); $this->writer->writeAttribute('no', '1'); $this->previousToken = $tokens[0]; foreach ($tokens as $token) { $this->addToken($token); } $this->writer->endElement(); $this->writer->endElement(); $this->writer->endDocument(); return $this->writer->outputMemory(); } /** * @param Token $token */ private function addToken(Token $token) { if ($this->previousToken->getLine() < $token->getLine()) { $this->writer->endElement(); $this->writer->startElement('line'); $this->writer->writeAttribute('no', (string)$token->getLine()); $this->previousToken = $token; } if ($token->getValue() !== '') { $this->writer->startElement('token'); $this->writer->writeAttribute('name', $token->getName()); $this->writer->writeRaw(htmlspecialchars($token->getValue(), ENT_NOQUOTES | ENT_DISALLOWED | ENT_XML1)); $this->writer->endElement(); } } } 'T_OPEN_BRACKET', ')' => 'T_CLOSE_BRACKET', '[' => 'T_OPEN_SQUARE', ']' => 'T_CLOSE_SQUARE', '{' => 'T_OPEN_CURLY', '}' => 'T_CLOSE_CURLY', ';' => 'T_SEMICOLON', '.' => 'T_DOT', ',' => 'T_COMMA', '=' => 'T_EQUAL', '<' => 'T_LT', '>' => 'T_GT', '+' => 'T_PLUS', '-' => 'T_MINUS', '*' => 'T_MULT', '/' => 'T_DIV', '?' => 'T_QUESTION_MARK', '!' => 'T_EXCLAMATION_MARK', ':' => 'T_COLON', '"' => 'T_DOUBLE_QUOTES', '@' => 'T_AT', '&' => 'T_AMPERSAND', '%' => 'T_PERCENT', '|' => 'T_PIPE', '$' => 'T_DOLLAR', '^' => 'T_CARET', '~' => 'T_TILDE', '`' => 'T_BACKTICK' ]; public function parse(string $source): TokenCollection { $result = new TokenCollection(); if ($source === '') { return $result; } $tokens = token_get_all($source); $lastToken = new Token( $tokens[0][2], 'Placeholder', '' ); foreach ($tokens as $pos => $tok) { if (is_string($tok)) { $token = new Token( $lastToken->getLine(), $this->map[$tok], $tok ); $result->addToken($token); $lastToken = $token; continue; } $line = $tok[2]; $values = preg_split('/\R+/Uu', $tok[1]); foreach ($values as $v) { $token = new Token( $line, token_name($tok[0]), $v ); $result->addToken($token); $line++; $lastToken = $token; } } return $result; } } line = $line; $this->name = $name; $this->value = $value; } /** * @return int */ public function getLine(): int { return $this->line; } /** * @return string */ public function getName(): string { return $this->name; } /** * @return string */ public function getValue(): string { return $this->value; } } Tokenizer Copyright (c) 2017 Arne Blankerts and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Arne Blankerts nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A caching factory for token stream objects. */ class PHP_Token_Stream_CachingFactory { /** * @var array */ protected static $cache = []; /** * @param string $filename * * @return PHP_Token_Stream */ public static function get($filename) { if (!isset(self::$cache[$filename])) { self::$cache[$filename] = new PHP_Token_Stream($filename); } return self::$cache[$filename]; } /** * @param string $filename */ public static function clear($filename = null) { if (is_string($filename)) { unset(self::$cache[$filename]); } else { self::$cache = []; } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A stream of PHP tokens. */ class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator { /** * @var array */ protected static $customTokens = [ '(' => 'PHP_Token_OPEN_BRACKET', ')' => 'PHP_Token_CLOSE_BRACKET', '[' => 'PHP_Token_OPEN_SQUARE', ']' => 'PHP_Token_CLOSE_SQUARE', '{' => 'PHP_Token_OPEN_CURLY', '}' => 'PHP_Token_CLOSE_CURLY', ';' => 'PHP_Token_SEMICOLON', '.' => 'PHP_Token_DOT', ',' => 'PHP_Token_COMMA', '=' => 'PHP_Token_EQUAL', '<' => 'PHP_Token_LT', '>' => 'PHP_Token_GT', '+' => 'PHP_Token_PLUS', '-' => 'PHP_Token_MINUS', '*' => 'PHP_Token_MULT', '/' => 'PHP_Token_DIV', '?' => 'PHP_Token_QUESTION_MARK', '!' => 'PHP_Token_EXCLAMATION_MARK', ':' => 'PHP_Token_COLON', '"' => 'PHP_Token_DOUBLE_QUOTES', '@' => 'PHP_Token_AT', '&' => 'PHP_Token_AMPERSAND', '%' => 'PHP_Token_PERCENT', '|' => 'PHP_Token_PIPE', '$' => 'PHP_Token_DOLLAR', '^' => 'PHP_Token_CARET', '~' => 'PHP_Token_TILDE', '`' => 'PHP_Token_BACKTICK' ]; /** * @var string */ protected $filename; /** * @var array */ protected $tokens = []; /** * @var int */ protected $position = 0; /** * @var array */ protected $linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; /** * @var array */ protected $classes; /** * @var array */ protected $functions; /** * @var array */ protected $includes; /** * @var array */ protected $interfaces; /** * @var array */ protected $traits; /** * @var array */ protected $lineToFunctionMap = []; /** * Constructor. * * @param string $sourceCode */ public function __construct($sourceCode) { if (is_file($sourceCode)) { $this->filename = $sourceCode; $sourceCode = file_get_contents($sourceCode); } $this->scan($sourceCode); } /** * Destructor. */ public function __destruct() { $this->tokens = []; } /** * @return string */ public function __toString() { $buffer = ''; foreach ($this as $token) { $buffer .= $token; } return $buffer; } /** * @return string */ public function getFilename() { return $this->filename; } /** * Scans the source for sequences of characters and converts them into a * stream of tokens. * * @param string $sourceCode */ protected function scan($sourceCode) { $id = 0; $line = 1; $tokens = token_get_all($sourceCode); $numTokens = count($tokens); $lastNonWhitespaceTokenWasDoubleColon = false; for ($i = 0; $i < $numTokens; ++$i) { $token = $tokens[$i]; $skip = 0; if (is_array($token)) { $name = substr(token_name($token[0]), 2); $text = $token[1]; if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') { $name = 'CLASS_NAME_CONSTANT'; } elseif ($name == 'USE' && isset($tokens[$i + 2][0]) && $tokens[$i + 2][0] == T_FUNCTION) { $name = 'USE_FUNCTION'; $text .= $tokens[$i + 1][1] . $tokens[$i + 2][1]; $skip = 2; } $tokenClass = 'PHP_Token_' . $name; } else { $text = $token; $tokenClass = self::$customTokens[$token]; } $this->tokens[] = new $tokenClass($text, $line, $this, $id++); $lines = substr_count($text, "\n"); $line += $lines; if ($tokenClass == 'PHP_Token_HALT_COMPILER') { break; } elseif ($tokenClass == 'PHP_Token_COMMENT' || $tokenClass == 'PHP_Token_DOC_COMMENT') { $this->linesOfCode['cloc'] += $lines + 1; } if ($name == 'DOUBLE_COLON') { $lastNonWhitespaceTokenWasDoubleColon = true; } elseif ($name != 'WHITESPACE') { $lastNonWhitespaceTokenWasDoubleColon = false; } $i += $skip; } $this->linesOfCode['loc'] = substr_count($sourceCode, "\n"); $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - $this->linesOfCode['cloc']; } /** * @return int */ public function count() { return count($this->tokens); } /** * @return PHP_Token[] */ public function tokens() { return $this->tokens; } /** * @return array */ public function getClasses() { if ($this->classes !== null) { return $this->classes; } $this->parse(); return $this->classes; } /** * @return array */ public function getFunctions() { if ($this->functions !== null) { return $this->functions; } $this->parse(); return $this->functions; } /** * @return array */ public function getInterfaces() { if ($this->interfaces !== null) { return $this->interfaces; } $this->parse(); return $this->interfaces; } /** * @return array */ 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 */ public function getIncludes($categorize = false, $category = null) { if ($this->includes === null) { $this->includes = [ 'require_once' => [], 'require' => [], 'include_once' => [], 'include' => [] ]; 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]; } elseif ($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; } /** * Returns the name of the function or method a line belongs to. * * @return string or null if the line is not in a function or method */ public function getFunctionForLine($line) { $this->parse(); if (isset($this->lineToFunctionMap[$line])) { return $this->lineToFunctionMap[$line]; } } protected function parse() { $this->interfaces = []; $this->classes = []; $this->traits = []; $this->functions = []; $class = []; $classEndLine = []; $trait = false; $traitEndLine = false; $interface = false; $interfaceEndLine = false; foreach ($this->tokens as $token) { switch (get_class($token)) { case 'PHP_Token_HALT_COMPILER': return; case 'PHP_Token_INTERFACE': $interface = $token->getName(); $interfaceEndLine = $token->getEndLine(); $this->interfaces[$interface] = [ 'methods' => [], 'parent' => $token->getParent(), 'keywords' => $token->getKeywords(), 'docblock' => $token->getDocblock(), 'startLine' => $token->getLine(), 'endLine' => $interfaceEndLine, 'package' => $token->getPackage(), 'file' => $this->filename ]; break; case 'PHP_Token_CLASS': case 'PHP_Token_TRAIT': $tmp = [ 'methods' => [], '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[count($class) - 1]] = $tmp; } else { $trait = $token->getName(); $traitEndLine = $token->getEndLine(); $this->traits[$trait] = $tmp; } break; case 'PHP_Token_FUNCTION': $name = $token->getName(); $tmp = [ 'docblock' => $token->getDocblock(), 'keywords' => $token->getKeywords(), 'visibility'=> $token->getVisibility(), 'signature' => $token->getSignature(), 'startLine' => $token->getLine(), 'endLine' => $token->getEndLine(), 'ccn' => $token->getCCN(), 'file' => $this->filename ]; if (empty($class) && $trait === false && $interface === false) { $this->functions[$name] = $tmp; $this->addFunctionToMap( $name, $tmp['startLine'], $tmp['endLine'] ); } elseif (!empty($class)) { $this->classes[$class[count($class) - 1]]['methods'][$name] = $tmp; $this->addFunctionToMap( $class[count($class) - 1] . '::' . $name, $tmp['startLine'], $tmp['endLine'] ); } elseif ($trait !== false) { $this->traits[$trait]['methods'][$name] = $tmp; $this->addFunctionToMap( $trait . '::' . $name, $tmp['startLine'], $tmp['endLine'] ); } else { $this->interfaces[$interface]['methods'][$name] = $tmp; } break; case 'PHP_Token_CLOSE_CURLY': if (!empty($classEndLine) && $classEndLine[count($classEndLine) - 1] == $token->getLine()) { array_pop($classEndLine); array_pop($class); } elseif ($traitEndLine !== false && $traitEndLine == $token->getLine()) { $trait = false; $traitEndLine = false; } elseif ($interfaceEndLine !== false && $interfaceEndLine == $token->getLine()) { $interface = false; $interfaceEndLine = false; } break; } } } /** * @return array */ public function getLinesOfCode() { return $this->linesOfCode; } /** */ public function rewind() { $this->position = 0; } /** * @return bool */ public function valid() { return isset($this->tokens[$this->position]); } /** * @return int */ public function key() { return $this->position; } /** * @return PHP_Token */ public function current() { return $this->tokens[$this->position]; } /** */ public function next() { $this->position++; } /** * @param int $offset * * @return bool */ public function offsetExists($offset) { return isset($this->tokens[$offset]); } /** * @param int $offset * * @return mixed * * @throws OutOfBoundsException */ public function offsetGet($offset) { if (!$this->offsetExists($offset)) { throw new OutOfBoundsException( sprintf( 'No token at position "%s"', $offset ) ); } return $this->tokens[$offset]; } /** * @param int $offset * @param mixed $value */ public function offsetSet($offset, $value) { $this->tokens[$offset] = $value; } /** * @param int $offset * * @throws OutOfBoundsException */ public function offsetUnset($offset) { if (!$this->offsetExists($offset)) { throw new OutOfBoundsException( sprintf( 'No token at position "%s"', $offset ) ); } unset($this->tokens[$offset]); } /** * Seek to an absolute position. * * @param int $position * * @throws OutOfBoundsException */ public function seek($position) { $this->position = $position; if (!$this->valid()) { throw new OutOfBoundsException( sprintf( 'No token at position "%s"', $this->position ) ); } } /** * @param string $name * @param int $startLine * @param int $endLine */ private function addFunctionToMap($name, $startLine, $endLine) { for ($line = $startLine; $line <= $endLine; $line++) { $this->lineToFunctionMap[$line] = $name; } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A PHP token. */ abstract class PHP_Token { /** * @var string */ protected $text; /** * @var int */ protected $line; /** * @var PHP_Token_Stream */ protected $tokenStream; /** * @var int */ protected $id; /** * @param string $text * @param int $line * @param PHP_Token_Stream $tokenStream * @param int $id */ public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id) { $this->text = $text; $this->line = $line; $this->tokenStream = $tokenStream; $this->id = $id; } /** * @return string */ public function __toString() { return $this->text; } /** * @return int */ public function getLine() { return $this->line; } /** * @return int */ public function getId() { return $this->id; } } abstract class PHP_TokenWithScope extends PHP_Token { /** * @var int */ protected $endTokenId; /** * Get the docblock for this token * * This method will fetch the docblock belonging to the current token. The * docblock must be placed on the line directly above the token to be * recognized. * * @return string|null Returns the docblock as a string if found */ public function getDocblock() { $tokens = $this->tokenStream->tokens(); $currentLineNumber = $tokens[$this->id]->getLine(); $prevLineNumber = $currentLineNumber - 1; for ($i = $this->id - 1; $i; $i--) { if (!isset($tokens[$i])) { return; } if ($tokens[$i] instanceof PHP_Token_FUNCTION || $tokens[$i] instanceof PHP_Token_CLASS || $tokens[$i] instanceof PHP_Token_TRAIT) { // Some other trait, class or function, no docblock can be // used for the current token break; } $line = $tokens[$i]->getLine(); if ($line == $currentLineNumber || ($line == $prevLineNumber && $tokens[$i] instanceof PHP_Token_WHITESPACE)) { continue; } if ($line < $currentLineNumber && !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) { break; } return (string) $tokens[$i]; } } /** * @return int */ public function getEndTokenId() { $block = 0; $i = $this->id; $tokens = $this->tokenStream->tokens(); while ($this->endTokenId === null && isset($tokens[$i])) { if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || $tokens[$i] instanceof PHP_Token_DOLLAR_OPEN_CURLY_BRACES || $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { $block++; } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) { $block--; if ($block === 0) { $this->endTokenId = $i; } } elseif (($this instanceof PHP_Token_FUNCTION || $this instanceof PHP_Token_NAMESPACE) && $tokens[$i] instanceof PHP_Token_SEMICOLON) { if ($block === 0) { $this->endTokenId = $i; } } $i++; } if ($this->endTokenId === null) { $this->endTokenId = $this->id; } return $this->endTokenId; } /** * @return int */ public function getEndLine() { return $this->tokenStream[$this->getEndTokenId()]->getLine(); } } abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope { /** * @return string */ 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; } } } /** * @return string */ public function getKeywords() { $keywords = []; $tokens = $this->tokenStream->tokens(); for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { if (isset($tokens[$i]) && ($tokens[$i] instanceof PHP_Token_PRIVATE || $tokens[$i] instanceof PHP_Token_PROTECTED || $tokens[$i] instanceof PHP_Token_PUBLIC)) { continue; } if (isset($tokens[$i]) && ($tokens[$i] instanceof PHP_Token_STATIC || $tokens[$i] instanceof PHP_Token_FINAL || $tokens[$i] instanceof PHP_Token_ABSTRACT)) { $keywords[] = strtolower( str_replace('PHP_Token_', '', get_class($tokens[$i])) ); } } return implode(',', $keywords); } } abstract class PHP_Token_Includes extends PHP_Token { /** * @var string */ protected $name; /** * @var string */ protected $type; /** * @return string */ public function getName() { if ($this->name === null) { $this->process(); } return $this->name; } /** * @return string */ public function getType() { if ($this->type === null) { $this->process(); } return $this->type; } private function process() { $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])) ); } } } class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility { /** * @var array */ protected $arguments; /** * @var int */ protected $ccn; /** * @var string */ protected $name; /** * @var string */ protected $signature; /** * @var bool */ private $anonymous = false; /** * @return array */ public function getArguments() { if ($this->arguments !== null) { return $this->arguments; } $this->arguments = []; $tokens = $this->tokenStream->tokens(); $typeDeclaration = null; // Search for first token inside brackets $i = $this->id + 2; while (!$tokens[$i - 1] instanceof PHP_Token_OPEN_BRACKET) { $i++; } while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { if ($tokens[$i] instanceof PHP_Token_STRING) { $typeDeclaration = (string) $tokens[$i]; } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) { $this->arguments[(string) $tokens[$i]] = $typeDeclaration; $typeDeclaration = null; } $i++; } return $this->arguments; } /** * @return string */ public function getName() { if ($this->name !== null) { return $this->name; } $tokens = $this->tokenStream->tokens(); $i = $this->id + 1; if ($tokens[$i] instanceof PHP_Token_WHITESPACE) { $i++; } if ($tokens[$i] instanceof PHP_Token_AMPERSAND) { $i++; } if ($tokens[$i + 1] instanceof PHP_Token_OPEN_BRACKET) { $this->name = (string) $tokens[$i]; } elseif ($tokens[$i + 1] instanceof PHP_Token_WHITESPACE && $tokens[$i + 2] instanceof PHP_Token_OPEN_BRACKET) { $this->name = (string) $tokens[$i]; } else { $this->anonymous = true; $this->name = sprintf( 'anonymousFunction:%s#%s', $this->getLine(), $this->getId() ); } if (!$this->isAnonymous()) { 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; } /** * @return int */ public function getCCN() { if ($this->ccn !== null) { return $this->ccn; } $this->ccn = 1; $end = $this->getEndTokenId(); $tokens = $this->tokenStream->tokens(); for ($i = $this->id; $i <= $end; $i++) { switch (get_class($tokens[$i])) { case 'PHP_Token_IF': case 'PHP_Token_ELSEIF': case 'PHP_Token_FOR': case 'PHP_Token_FOREACH': case 'PHP_Token_WHILE': case 'PHP_Token_CASE': case 'PHP_Token_CATCH': case 'PHP_Token_BOOLEAN_AND': case 'PHP_Token_LOGICAL_AND': case 'PHP_Token_BOOLEAN_OR': case 'PHP_Token_LOGICAL_OR': case 'PHP_Token_QUESTION_MARK': $this->ccn++; break; } } return $this->ccn; } /** * @return string */ public function getSignature() { if ($this->signature !== null) { return $this->signature; } if ($this->isAnonymous()) { $this->signature = 'anonymousFunction'; $i = $this->id + 1; } else { $this->signature = ''; $i = $this->id + 2; } $tokens = $this->tokenStream->tokens(); while (isset($tokens[$i]) && !$tokens[$i] instanceof PHP_Token_OPEN_CURLY && !$tokens[$i] instanceof PHP_Token_SEMICOLON) { $this->signature .= $tokens[$i++]; } $this->signature = trim($this->signature); return $this->signature; } /** * @return bool */ public function isAnonymous() { return $this->anonymous; } } class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility { /** * @var array */ protected $interfaces; /** * @return string */ public function getName() { return (string) $this->tokenStream[$this->id + 2]; } /** * @return bool */ public function hasParent() { return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS; } /** * @return array */ public function getPackage() { $className = $this->getName(); $docComment = $this->getDocblock(); $result = [ '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; } /** * @param array $parts * @param string $join * * @return string */ protected function arrayToName(array $parts, $join = '\\') { $result = ''; if (count($parts) > 1) { array_pop($parts); $result = implode($join, $parts); } return $result; } /** * @return bool|string */ public function getParent() { if (!$this->hasParent()) { return false; } $i = $this->id + 6; $tokens = $this->tokenStream->tokens(); $className = (string) $tokens[$i]; while (isset($tokens[$i + 1]) && !$tokens[$i + 1] instanceof PHP_Token_WHITESPACE) { $className .= (string) $tokens[++$i]; } return $className; } /** * @return bool */ 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); } /** * @return array|bool */ 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_ABSTRACT extends PHP_Token { } class PHP_Token_AMPERSAND extends PHP_Token { } class PHP_Token_AND_EQUAL extends PHP_Token { } class PHP_Token_ARRAY extends PHP_Token { } class PHP_Token_ARRAY_CAST extends PHP_Token { } class PHP_Token_AS extends PHP_Token { } class PHP_Token_AT extends PHP_Token { } class PHP_Token_BACKTICK extends PHP_Token { } class PHP_Token_BAD_CHARACTER extends PHP_Token { } class PHP_Token_BOOLEAN_AND extends PHP_Token { } class PHP_Token_BOOLEAN_OR extends PHP_Token { } class PHP_Token_BOOL_CAST extends PHP_Token { } class PHP_Token_BREAK extends PHP_Token { } class PHP_Token_CARET extends PHP_Token { } class PHP_Token_CASE extends PHP_Token { } class PHP_Token_CATCH extends PHP_Token { } class PHP_Token_CHARACTER extends PHP_Token { } class PHP_Token_CLASS extends PHP_Token_INTERFACE { /** * @var bool */ private $anonymous = false; /** * @var string */ private $name; /** * @return string */ public function getName() { if ($this->name !== null) { return $this->name; } $next = $this->tokenStream[$this->id + 1]; if ($next instanceof PHP_Token_WHITESPACE) { $next = $this->tokenStream[$this->id + 2]; } if ($next instanceof PHP_Token_STRING) { $this->name =(string) $next; return $this->name; } if ($next instanceof PHP_Token_OPEN_CURLY || $next instanceof PHP_Token_EXTENDS || $next instanceof PHP_Token_IMPLEMENTS) { $this->name = sprintf( 'AnonymousClass:%s#%s', $this->getLine(), $this->getId() ); $this->anonymous = true; return $this->name; } } public function isAnonymous() { return $this->anonymous; } } class PHP_Token_CLASS_C extends PHP_Token { } class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token { } class PHP_Token_CLONE extends PHP_Token { } class PHP_Token_CLOSE_BRACKET extends PHP_Token { } class PHP_Token_CLOSE_CURLY extends PHP_Token { } class PHP_Token_CLOSE_SQUARE extends PHP_Token { } class PHP_Token_CLOSE_TAG extends PHP_Token { } class PHP_Token_COLON extends PHP_Token { } class PHP_Token_COMMA extends PHP_Token { } class PHP_Token_COMMENT extends PHP_Token { } class PHP_Token_CONCAT_EQUAL extends PHP_Token { } class PHP_Token_CONST extends PHP_Token { } class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token { } class PHP_Token_CONTINUE extends PHP_Token { } class PHP_Token_CURLY_OPEN extends PHP_Token { } class PHP_Token_DEC extends PHP_Token { } class PHP_Token_DECLARE extends PHP_Token { } class PHP_Token_DEFAULT extends PHP_Token { } class PHP_Token_DIV extends PHP_Token { } class PHP_Token_DIV_EQUAL extends PHP_Token { } class PHP_Token_DNUMBER extends PHP_Token { } class PHP_Token_DO extends PHP_Token { } class PHP_Token_DOC_COMMENT extends PHP_Token { } class PHP_Token_DOLLAR extends PHP_Token { } class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token { } class PHP_Token_DOT extends PHP_Token { } class PHP_Token_DOUBLE_ARROW extends PHP_Token { } class PHP_Token_DOUBLE_CAST extends PHP_Token { } class PHP_Token_DOUBLE_COLON extends PHP_Token { } class PHP_Token_DOUBLE_QUOTES extends PHP_Token { } class PHP_Token_ECHO extends PHP_Token { } class PHP_Token_ELSE extends PHP_Token { } class PHP_Token_ELSEIF extends PHP_Token { } class PHP_Token_EMPTY extends PHP_Token { } class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token { } class PHP_Token_ENDDECLARE extends PHP_Token { } class PHP_Token_ENDFOR extends PHP_Token { } class PHP_Token_ENDFOREACH extends PHP_Token { } class PHP_Token_ENDIF extends PHP_Token { } class PHP_Token_ENDSWITCH extends PHP_Token { } class PHP_Token_ENDWHILE extends PHP_Token { } class PHP_Token_END_HEREDOC extends PHP_Token { } class PHP_Token_EQUAL extends PHP_Token { } class PHP_Token_EVAL extends PHP_Token { } class PHP_Token_EXCLAMATION_MARK extends PHP_Token { } class PHP_Token_EXIT extends PHP_Token { } class PHP_Token_EXTENDS extends PHP_Token { } class PHP_Token_FILE extends PHP_Token { } class PHP_Token_FINAL extends PHP_Token { } class PHP_Token_FOR extends PHP_Token { } class PHP_Token_FOREACH extends PHP_Token { } class PHP_Token_FUNC_C extends PHP_Token { } class PHP_Token_GLOBAL extends PHP_Token { } class PHP_Token_GT extends PHP_Token { } class PHP_Token_IF extends PHP_Token { } class PHP_Token_IMPLEMENTS extends PHP_Token { } class PHP_Token_INC extends PHP_Token { } class PHP_Token_INCLUDE extends PHP_Token_Includes { } class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes { } class PHP_Token_INLINE_HTML extends PHP_Token { } class PHP_Token_INSTANCEOF extends PHP_Token { } class PHP_Token_INT_CAST extends PHP_Token { } class PHP_Token_ISSET extends PHP_Token { } class PHP_Token_IS_EQUAL extends PHP_Token { } class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token { } class PHP_Token_IS_IDENTICAL extends PHP_Token { } class PHP_Token_IS_NOT_EQUAL extends PHP_Token { } class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token { } class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token { } class PHP_Token_LINE extends PHP_Token { } class PHP_Token_LIST extends PHP_Token { } class PHP_Token_LNUMBER extends PHP_Token { } class PHP_Token_LOGICAL_AND extends PHP_Token { } class PHP_Token_LOGICAL_OR extends PHP_Token { } class PHP_Token_LOGICAL_XOR extends PHP_Token { } class PHP_Token_LT extends PHP_Token { } class PHP_Token_METHOD_C extends PHP_Token { } class PHP_Token_MINUS extends PHP_Token { } class PHP_Token_MINUS_EQUAL extends PHP_Token { } class PHP_Token_MOD_EQUAL extends PHP_Token { } class PHP_Token_MULT extends PHP_Token { } class PHP_Token_MUL_EQUAL extends PHP_Token { } class PHP_Token_NEW extends PHP_Token { } class PHP_Token_NUM_STRING extends PHP_Token { } class PHP_Token_OBJECT_CAST extends PHP_Token { } class PHP_Token_OBJECT_OPERATOR extends PHP_Token { } class PHP_Token_OPEN_BRACKET extends PHP_Token { } class PHP_Token_OPEN_CURLY extends PHP_Token { } class PHP_Token_OPEN_SQUARE extends PHP_Token { } class PHP_Token_OPEN_TAG extends PHP_Token { } class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token { } class PHP_Token_OR_EQUAL extends PHP_Token { } class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token { } class PHP_Token_PERCENT extends PHP_Token { } class PHP_Token_PIPE extends PHP_Token { } class PHP_Token_PLUS extends PHP_Token { } class PHP_Token_PLUS_EQUAL extends PHP_Token { } class PHP_Token_PRINT extends PHP_Token { } class PHP_Token_PRIVATE extends PHP_Token { } class PHP_Token_PROTECTED extends PHP_Token { } class PHP_Token_PUBLIC extends PHP_Token { } class PHP_Token_QUESTION_MARK extends PHP_Token { } class PHP_Token_REQUIRE extends PHP_Token_Includes { } class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes { } class PHP_Token_RETURN extends PHP_Token { } class PHP_Token_SEMICOLON extends PHP_Token { } class PHP_Token_SL extends PHP_Token { } class PHP_Token_SL_EQUAL extends PHP_Token { } class PHP_Token_SR extends PHP_Token { } class PHP_Token_SR_EQUAL extends PHP_Token { } class PHP_Token_START_HEREDOC extends PHP_Token { } class PHP_Token_STATIC extends PHP_Token { } class PHP_Token_STRING extends PHP_Token { } class PHP_Token_STRING_CAST extends PHP_Token { } class PHP_Token_STRING_VARNAME extends PHP_Token { } class PHP_Token_SWITCH extends PHP_Token { } class PHP_Token_THROW extends PHP_Token { } class PHP_Token_TILDE extends PHP_Token { } class PHP_Token_TRY extends PHP_Token { } class PHP_Token_UNSET extends PHP_Token { } class PHP_Token_UNSET_CAST extends PHP_Token { } class PHP_Token_USE extends PHP_Token { } class PHP_Token_USE_FUNCTION extends PHP_Token { } class PHP_Token_VAR extends PHP_Token { } class PHP_Token_VARIABLE extends PHP_Token { } class PHP_Token_WHILE extends PHP_Token { } class PHP_Token_WHITESPACE extends PHP_Token { } class PHP_Token_XOR_EQUAL extends PHP_Token { } // Tokens introduced in PHP 5.1 class PHP_Token_HALT_COMPILER extends PHP_Token { } // Tokens introduced in PHP 5.3 class PHP_Token_DIR extends PHP_Token { } class PHP_Token_GOTO extends PHP_Token { } class PHP_Token_NAMESPACE extends PHP_TokenWithScope { /** * @return string */ public function getName() { $tokens = $this->tokenStream->tokens(); $namespace = (string) $tokens[$this->id + 2]; for ($i = $this->id + 3;; $i += 2) { if (isset($tokens[$i]) && $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) { $namespace .= '\\' . $tokens[$i + 1]; } else { break; } } return $namespace; } } class PHP_Token_NS_C extends PHP_Token { } class PHP_Token_NS_SEPARATOR extends PHP_Token { } // Tokens introduced in PHP 5.4 class PHP_Token_CALLABLE extends PHP_Token { } class PHP_Token_INSTEADOF extends PHP_Token { } class PHP_Token_TRAIT extends PHP_Token_INTERFACE { } class PHP_Token_TRAIT_C extends PHP_Token { } // Tokens introduced in PHP 5.5 class PHP_Token_FINALLY extends PHP_Token { } class PHP_Token_YIELD extends PHP_Token { } // Tokens introduced in PHP 5.6 class PHP_Token_ELLIPSIS extends PHP_Token { } class PHP_Token_POW extends PHP_Token { } class PHP_Token_POW_EQUAL extends PHP_Token { } // Tokens introduced in PHP 7.0 class PHP_Token_COALESCE extends PHP_Token { } class PHP_Token_SPACESHIP extends PHP_Token { } class PHP_Token_YIELD_FROM extends PHP_Token { } php-token-stream Copyright (c) 2009-2018, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; /** * The location where an element occurs within a file. */ final class Location { /** @var int */ private $lineNumber = 0; /** @var int */ private $columnNumber = 0; /** * Initializes the location for an element using its line number in the file and optionally the column number. * * @param int $lineNumber * @param int $columnNumber */ public function __construct($lineNumber, $columnNumber = 0) { $this->lineNumber = $lineNumber; $this->columnNumber = $columnNumber; } /** * Returns the line number that is covered by this location. * * @return integer */ public function getLineNumber() { return $this->lineNumber; } /** * Returns the column number (character position on a line) for this location object. * * @return integer */ public function getColumnNumber() { return $this->columnNumber; } } fqsen = $fqsen; if (isset($matches[2])) { $this->name = $matches[2]; } else { $matches = explode('\\', $fqsen); $this->name = trim(end($matches), '()'); } } /** * converts this class to string. * * @return string */ public function __toString() { return $this->fqsen; } /** * Returns the name of the element without path. * * @return string */ public function getName() { return $this->name; } } The MIT License (MIT) Copyright (c) 2015 phpDocumentor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection; /** * Interface for files processed by the ProjectFactory */ interface File { /** * Returns the content of the file as a string. * * @return string */ public function getContents(); /** * Returns md5 hash of the file. * * @return string */ public function md5(); /** * Returns an relative path to the file. * * @return string */ public function path(); } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; /** * Diff implementation. */ final class Differ { public const OLD = 0; public const ADDED = 1; public const REMOVED = 2; public const DIFF_LINE_END_WARNING = 3; public const NO_LINE_END_EOF_WARNING = 4; /** * @var DiffOutputBuilderInterface */ private $outputBuilder; /** * @param DiffOutputBuilderInterface $outputBuilder * * @throws InvalidArgumentException */ public function __construct($outputBuilder = null) { if ($outputBuilder instanceof DiffOutputBuilderInterface) { $this->outputBuilder = $outputBuilder; } elseif (null === $outputBuilder) { $this->outputBuilder = new UnifiedDiffOutputBuilder; } elseif (\is_string($outputBuilder)) { // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 // @deprecated $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); } else { throw new InvalidArgumentException( \sprintf( 'Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', \is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"' ) ); } } /** * Returns the diff between two arrays or strings as string. * * @param array|string $from * @param array|string $to * @param null|LongestCommonSubsequenceCalculator $lcs * * @return string */ public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string { $diff = $this->diffToArray( $this->normalizeDiffInput($from), $this->normalizeDiffInput($to), $lcs ); return $this->outputBuilder->getDiff($diff); } /** * Returns the diff between two arrays or strings as array. * * Each array element contains two elements: * - [0] => mixed $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 * @param LongestCommonSubsequenceCalculator $lcs * * @return array */ public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array { if (\is_string($from)) { $from = $this->splitStringByLines($from); } elseif (!\is_array($from)) { throw new InvalidArgumentException('"from" must be an array or string.'); } if (\is_string($to)) { $to = $this->splitStringByLines($to); } elseif (!\is_array($to)) { throw new InvalidArgumentException('"to" must be an array or string.'); } [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); if ($lcs === null) { $lcs = $this->selectLcsImplementation($from, $to); } $common = $lcs->calculate(\array_values($from), \array_values($to)); $diff = []; foreach ($start as $token) { $diff[] = [$token, self::OLD]; } \reset($from); \reset($to); foreach ($common as $token) { while (($fromToken = \reset($from)) !== $token) { $diff[] = [\array_shift($from), self::REMOVED]; } while (($toToken = \reset($to)) !== $token) { $diff[] = [\array_shift($to), self::ADDED]; } $diff[] = [$token, self::OLD]; \array_shift($from); \array_shift($to); } while (($token = \array_shift($from)) !== null) { $diff[] = [$token, self::REMOVED]; } while (($token = \array_shift($to)) !== null) { $diff[] = [$token, self::ADDED]; } foreach ($end as $token) { $diff[] = [$token, self::OLD]; } if ($this->detectUnmatchedLineEndings($diff)) { \array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); } return $diff; } /** * Casts variable to string if it is not a string or array. * * @param mixed $input * * @return array|string */ private function normalizeDiffInput($input) { if (!\is_array($input) && !\is_string($input)) { return (string) $input; } return $input; } /** * Checks if input is string, if so it will split it line-by-line. * * @param string $input * * @return array */ private function splitStringByLines(string $input): array { return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } /** * @param array $from * @param array $to * * @return LongestCommonSubsequenceCalculator */ private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator { // We do not want to use the time-efficient implementation if its memory // footprint will probably exceed this value. Note that the footprint // calculation is only an estimation for the matrix and the LCS method // will typically allocate a bit more memory than this. $memoryLimit = 100 * 1024 * 1024; if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { return new MemoryEfficientLongestCommonSubsequenceCalculator; } return new TimeEfficientLongestCommonSubsequenceCalculator; } /** * Calculates the estimated memory footprint for the DP-based method. * * @param array $from * @param array $to * * @return float|int */ private function calculateEstimatedFootprint(array $from, array $to) { $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; return $itemSize * \min(\count($from), \count($to)) ** 2; } /** * Returns true if line ends don't match in a diff. * * @param array $diff * * @return bool */ private function detectUnmatchedLineEndings(array $diff): bool { $newLineBreaks = ['' => true]; $oldLineBreaks = ['' => true]; foreach ($diff as $entry) { if (self::OLD === $entry[1]) { $ln = $this->getLinebreak($entry[0]); $oldLineBreaks[$ln] = true; $newLineBreaks[$ln] = true; } elseif (self::ADDED === $entry[1]) { $newLineBreaks[$this->getLinebreak($entry[0])] = true; } elseif (self::REMOVED === $entry[1]) { $oldLineBreaks[$this->getLinebreak($entry[0])] = true; } } // if either input or output is a single line without breaks than no warning should be raised if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { return false; } // two way compare foreach ($newLineBreaks as $break => $set) { if (!isset($oldLineBreaks[$break])) { return true; } } foreach ($oldLineBreaks as $break => $set) { if (!isset($newLineBreaks[$break])) { return true; } } return false; } private function getLinebreak($line): string { if (!\is_string($line)) { return ''; } $lc = \substr($line, -1); if ("\r" === $lc) { return "\r"; } if ("\n" !== $lc) { return ''; } if ("\r\n" === \substr($line, -2)) { return "\r\n"; } return "\n"; } private static function getArrayDiffParted(array &$from, array &$to): array { $start = []; $end = []; \reset($to); foreach ($from as $k => $v) { $toK = \key($to); if ($toK === $k && $v === $to[$k]) { $start[$k] = $v; unset($from[$k], $to[$k]); } else { break; } } \end($from); \end($to); do { $fromK = \key($from); $toK = \key($to); if (null === $fromK || null === $toK || \current($from) !== \current($to)) { break; } \prev($from); \prev($to); $end = [$fromK => $from[$fromK]] + $end; unset($from[$fromK], $to[$toK]); } while (true); return [$from, $to, $start, $end]; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; interface Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; class InvalidArgumentException extends \InvalidArgumentException implements Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class ConfigurationException extends InvalidArgumentException { /** * @param string $option * @param string $expected * @param mixed $value * @param int $code * @param null|\Exception $previous */ public function __construct( string $option, string $expected, $value, int $code = 0, \Exception $previous = null ) { parent::__construct( \sprintf( 'Option "%s" must be %s, got "%s".', $option, $expected, \is_object($value) ? \get_class($value) : (null === $value ? '' : \gettype($value) . '#' . $value) ), $code, $previous ); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Diff { /** * @var string */ private $from; /** * @var string */ private $to; /** * @var Chunk[] */ private $chunks; /** * @param string $from * @param string $to * @param Chunk[] $chunks */ public function __construct(string $from, string $to, array $chunks = []) { $this->from = $from; $this->to = $to; $this->chunks = $chunks; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } /** * @return Chunk[] */ public function getChunks(): array { return $this->chunks; } /** * @param Chunk[] $chunks */ public function setChunks(array $chunks): void { $this->chunks = $chunks; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; /** * Unified diff parser. */ final class Parser { /** * @param string $string * * @return Diff[] */ public function parse(string $string): array { $lines = \preg_split('(\r\n|\r|\n)', $string); if (!empty($lines) && $lines[\count($lines) - 1] === '') { \array_pop($lines); } $lineCount = \count($lines); $diffs = []; $diff = null; $collected = []; for ($i = 0; $i < $lineCount; ++$i) { if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { if ($diff !== null) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; $collected = []; } $diff = new Diff($fromMatch['file'], $toMatch['file']); ++$i; } else { if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { continue; } $collected[] = $lines[$i]; } } if ($diff !== null && \count($collected)) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; } return $diffs; } private function parseFileDiff(Diff $diff, array $lines): void { $chunks = []; $chunk = null; foreach ($lines as $line) { if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { $chunk = new Chunk( (int) $match['start'], isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 ); $chunks[] = $chunk; $diffLines = []; continue; } if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { $type = Line::UNCHANGED; if ($match['type'] === '+') { $type = Line::ADDED; } elseif ($match['type'] === '-') { $type = Line::REMOVED; } $diffLines[] = new Line($type, $match['line']); if (null !== $chunk) { $chunk->setLines($diffLines); } } } $diff->setChunks($chunks); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { /** * Takes input of the diff array and returns the common parts. * Iterates through diff line by line. * * @param array $diff * @param int $lineThreshold * * @return array */ protected function getCommonChunks(array $diff, int $lineThreshold = 5): array { $diffSize = \count($diff); $capturing = false; $chunkStart = 0; $chunkSize = 0; $commonChunks = []; for ($i = 0; $i < $diffSize; ++$i) { if ($diff[$i][1] === 0 /* OLD */) { if ($capturing === false) { $capturing = true; $chunkStart = $i; $chunkSize = 0; } else { ++$chunkSize; } } elseif ($capturing !== false) { if ($chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } $capturing = false; } } if ($capturing !== false && $chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } return $commonChunks; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; /** * Defines how an output builder should take a generated * diff array and return a string representation of that diff. */ interface DiffOutputBuilderInterface { public function getDiff(array $diff): string; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in unified diff format in chunks. */ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder { /** * @var bool */ private $collapseRanges = true; /** * @var int >= 0 */ private $commonLineThreshold = 6; /** * @var int >= 0 */ private $contextLines = 3; /** * @var string */ private $header; /** * @var bool */ private $addLineNumbers; public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false) { $this->header = $header; $this->addLineNumbers = $addLineNumbers; } public function getDiff(array $diff): string { $buffer = \fopen('php://memory', 'r+b'); if ('' !== $this->header) { \fwrite($buffer, $this->header); if ("\n" !== \substr($this->header, -1, 1)) { \fwrite($buffer, "\n"); } } if (0 !== \count($diff)) { $this->writeDiffHunks($buffer, $diff); } $diff = \stream_get_contents($buffer, -1, 0); \fclose($buffer); // If the last char is not a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = \substr($diff, -1); return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff ; } private function writeDiffHunks($output, array $diff): void { // detect "No newline at end of file" and insert into `$diff` if needed $upperLimit = \count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = \substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { // search back for the last `+` and `-` line, // check if has trailing linebreak, else add under it warning under it $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; --$i) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = \substr($diff[$i][0], -1); if ("\n" !== $lc) { \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!\count($toFind)) { break; } } } } // write hunks to output buffer $cutOff = \max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; foreach ($diff as $i => $entry) { if (0 === $entry[1]) { // same if (false === $hunkCapture) { ++$fromStart; ++$toStart; continue; } ++$sameCount; ++$toRange; ++$fromRange; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines ; // note: $contextEndOffset = $this->contextLines; // // because we never go beyond the end of the diff. // with the cutoff/contextlines here the follow is never true; // // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { // $contextEndOffset = count($diff) - 1; // } // // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { ++$toRange; } if (Differ::REMOVED === $entry[1]) { ++$fromRange; } } if (false === $hunkCapture) { return; } // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines ; // prevent trying to write out more common lines than there are in the diff _and_ // do not write more than configured through the context lines $contextEndOffset = \min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { if ($this->addLineNumbers) { \fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { \fwrite($output, ',' . $fromRange); } \fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { \fwrite($output, ',' . $toRange); } \fwrite($output, " @@\n"); } else { \fwrite($output, "@@ @@\n"); } for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { if ($diff[$i][1] === Differ::ADDED) { \fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { \fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { \fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { \fwrite($output, "\n"); // $diff[$i][0] } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ \fwrite($output, ' ' . $diff[$i][0]); } } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in a loose unified diff format * listing only changes lines. Does not include line numbers. */ final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface { /** * @var string */ private $header; public function __construct(string $header = "--- Original\n+++ New\n") { $this->header = $header; } public function getDiff(array $diff): string { $buffer = \fopen('php://memory', 'r+b'); if ('' !== $this->header) { \fwrite($buffer, $this->header); if ("\n" !== \substr($this->header, -1, 1)) { \fwrite($buffer, "\n"); } } foreach ($diff as $diffEntry) { if ($diffEntry[1] === Differ::ADDED) { \fwrite($buffer, '+' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::REMOVED) { \fwrite($buffer, '-' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { \fwrite($buffer, ' ' . $diffEntry[0]); continue; // Warnings should not be tested for line break, it will always be there } else { /* Not changed (old) 0 */ continue; // we didn't write the non changs line, so do not add a line break either } $lc = \substr($diffEntry[0], -1); if ($lc !== "\n" && $lc !== "\r") { \fwrite($buffer, "\n"); // \No newline at end of file } } $diff = \stream_get_contents($buffer, -1, 0); \fclose($buffer); return $diff; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use SebastianBergmann\Diff\ConfigurationException; use SebastianBergmann\Diff\Differ; /** * Strict Unified diff output builder. * * Generates (strict) Unified diff's (unidiffs) with hunks. */ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface { private static $default = [ 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 'fromFile' => null, 'fromFileDate' => null, 'toFile' => null, 'toFileDate' => null, ]; /** * @var bool */ private $changed; /** * @var bool */ private $collapseRanges; /** * @var int >= 0 */ private $commonLineThreshold; /** * @var string */ private $header; /** * @var int >= 0 */ private $contextLines; public function __construct(array $options = []) { $options = \array_merge(self::$default, $options); if (!\is_bool($options['collapseRanges'])) { throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); } if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); } if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); } foreach (['fromFile', 'toFile'] as $option) { if (!\is_string($options[$option])) { throw new ConfigurationException($option, 'a string', $options[$option]); } } foreach (['fromFileDate', 'toFileDate'] as $option) { if (null !== $options[$option] && !\is_string($options[$option])) { throw new ConfigurationException($option, 'a string or ', $options[$option]); } } $this->header = \sprintf( "--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] ); $this->collapseRanges = $options['collapseRanges']; $this->commonLineThreshold = $options['commonLineThreshold']; $this->contextLines = $options['contextLines']; } public function getDiff(array $diff): string { if (0 === \count($diff)) { return ''; } $this->changed = false; $buffer = \fopen('php://memory', 'r+b'); \fwrite($buffer, $this->header); $this->writeDiffHunks($buffer, $diff); if (!$this->changed) { \fclose($buffer); return ''; } $diff = \stream_get_contents($buffer, -1, 0); \fclose($buffer); // If the last char is not a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = \substr($diff, -1); return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff ; } private function writeDiffHunks($output, array $diff): void { // detect "No newline at end of file" and insert into `$diff` if needed $upperLimit = \count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = \substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { // search back for the last `+` and `-` line, // check if has trailing linebreak, else add under it warning under it $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; --$i) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = \substr($diff[$i][0], -1); if ("\n" !== $lc) { \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!\count($toFind)) { break; } } } } // write hunks to output buffer $cutOff = \max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; foreach ($diff as $i => $entry) { if (0 === $entry[1]) { // same if (false === $hunkCapture) { ++$fromStart; ++$toStart; continue; } ++$sameCount; ++$toRange; ++$fromRange; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines ; // note: $contextEndOffset = $this->contextLines; // // because we never go beyond the end of the diff. // with the cutoff/contextlines here the follow is never true; // // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { // $contextEndOffset = count($diff) - 1; // } // // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } $this->changed = true; if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { // added ++$toRange; } if (Differ::REMOVED === $entry[1]) { // removed ++$fromRange; } } if (false === $hunkCapture) { return; } // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines ; // prevent trying to write out more common lines than there are in the diff _and_ // do not write more than configured through the context lines $contextEndOffset = \min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { \fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { \fwrite($output, ',' . $fromRange); } \fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { \fwrite($output, ',' . $toRange); } \fwrite($output, " @@\n"); for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { if ($diff[$i][1] === Differ::ADDED) { $this->changed = true; \fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { $this->changed = true; \fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { \fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { $this->changed = true; \fwrite($output, $diff[$i][0]); } //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package // skip //} else { // unknown/invalid //} } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Line { public const ADDED = 1; public const REMOVED = 2; public const UNCHANGED = 3; /** * @var int */ private $type; /** * @var string */ private $content; public function __construct(int $type = self::UNCHANGED, string $content = '') { $this->type = $type; $this->content = $content; } public function getContent(): string { return $this->content; } public function getType(): int { return $this->type; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * {@inheritdoc} */ public function calculate(array $from, array $to): array { $common = []; $fromLength = \count($from); $toLength = \count($to); $width = $fromLength + 1; $matrix = new \SplFixedArray($width * ($toLength + 1)); for ($i = 0; $i <= $fromLength; ++$i) { $matrix[$i] = 0; } for ($j = 0; $j <= $toLength; ++$j) { $matrix[$j * $width] = 0; } for ($i = 1; $i <= $fromLength; ++$i) { for ($j = 1; $j <= $toLength; ++$j) { $o = ($j * $width) + $i; $matrix[$o] = \max( $matrix[$o - 1], $matrix[$o - $width], $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 ); } } $i = $fromLength; $j = $toLength; while ($i > 0 && $j > 0) { if ($from[$i - 1] === $to[$j - 1]) { $common[] = $from[$i - 1]; --$i; --$j; } else { $o = ($j * $width) + $i; if ($matrix[$o - $width] > $matrix[$o - 1]) { --$j; } else { --$i; } } } return \array_reverse($common); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Chunk { /** * @var int */ private $start; /** * @var int */ private $startRange; /** * @var int */ private $end; /** * @var int */ private $endRange; /** * @var Line[] */ private $lines; public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) { $this->start = $start; $this->startRange = $startRange; $this->end = $end; $this->endRange = $endRange; $this->lines = $lines; } public function getStart(): int { return $this->start; } public function getStartRange(): int { return $this->startRange; } public function getEnd(): int { return $this->end; } public function getEndRange(): int { return $this->endRange; } /** * @return Line[] */ public function getLines(): array { return $this->lines; } /** * @param Line[] $lines */ public function setLines(array $lines): void { foreach ($lines as $line) { if (!$line instanceof Line) { throw new InvalidArgumentException; } } $this->lines = $lines; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * {@inheritdoc} */ public function calculate(array $from, array $to): array { $cFrom = \count($from); $cTo = \count($to); if ($cFrom === 0) { return []; } if ($cFrom === 1) { if (\in_array($from[0], $to, true)) { return [$from[0]]; } return []; } $i = (int) ($cFrom / 2); $fromStart = \array_slice($from, 0, $i); $fromEnd = \array_slice($from, $i); $llB = $this->length($fromStart, $to); $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); $jMax = 0; $max = 0; for ($j = 0; $j <= $cTo; $j++) { $m = $llB[$j] + $llE[$cTo - $j]; if ($m >= $max) { $max = $m; $jMax = $j; } } $toStart = \array_slice($to, 0, $jMax); $toEnd = \array_slice($to, $jMax); return \array_merge( $this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd) ); } private function length(array $from, array $to): array { $current = \array_fill(0, \count($to) + 1, 0); $cFrom = \count($from); $cTo = \count($to); for ($i = 0; $i < $cFrom; $i++) { $prev = $current; for ($j = 0; $j < $cTo; $j++) { if ($from[$i] === $to[$j]) { $current[$j + 1] = $prev[$j] + 1; } else { $current[$j + 1] = \max($current[$j], $prev[$j + 1]); } } } return $current; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; interface LongestCommonSubsequenceCalculator { /** * Calculates the longest common subsequence of two arrays. * * @param array $from * @param array $to * * @return array */ public function calculate(array $from, array $to): array; } sebastian/diff Copyright (c) 2002-2019, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\PhptTestCase; use PHPUnit\Util\Test; use SebastianBergmann\CodeCoverage\Driver\Driver; use SebastianBergmann\CodeCoverage\Driver\PCOV; use SebastianBergmann\CodeCoverage\Driver\PHPDBG; use SebastianBergmann\CodeCoverage\Driver\Xdebug; use SebastianBergmann\CodeCoverage\Node\Builder; use SebastianBergmann\CodeCoverage\Node\Directory; use SebastianBergmann\CodeUnitReverseLookup\Wizard; use SebastianBergmann\Environment\Runtime; /** * Provides collection functionality for PHP code coverage information. */ final class CodeCoverage { /** * @var Driver */ private $driver; /** * @var Filter */ private $filter; /** * @var Wizard */ private $wizard; /** * @var bool */ private $cacheTokens = false; /** * @var bool */ private $checkForUnintentionallyCoveredCode = false; /** * @var bool */ private $forceCoversAnnotation = false; /** * @var bool */ private $checkForUnexecutedCoveredCode = false; /** * @var bool */ private $checkForMissingCoversAnnotation = false; /** * @var bool */ private $addUncoveredFilesFromWhitelist = true; /** * @var bool */ private $processUncoveredFilesFromWhitelist = false; /** * @var bool */ private $ignoreDeprecatedCode = false; /** * @var PhptTestCase|string|TestCase */ private $currentId; /** * Code coverage data. * * @var array */ private $data = []; /** * @var array */ private $ignoredLines = []; /** * @var bool */ private $disableIgnoredLines = false; /** * Test data. * * @var array */ private $tests = []; /** * @var string[] */ private $unintentionallyCoveredSubclassesWhitelist = []; /** * Determine if the data has been initialized or not * * @var bool */ private $isInitialized = false; /** * Determine whether we need to check for dead and unused code on each test * * @var bool */ private $shouldCheckForDeadAndUnused = true; /** * @var Directory */ private $report; /** * @throws RuntimeException */ public function __construct(Driver $driver = null, Filter $filter = null) { if ($filter === null) { $filter = new Filter; } if ($driver === null) { $driver = $this->selectDriver($filter); } $this->driver = $driver; $this->filter = $filter; $this->wizard = new Wizard; } /** * Returns the code coverage information as a graph of node objects. */ public function getReport(): Directory { if ($this->report === null) { $builder = new Builder; $this->report = $builder->build($this); } return $this->report; } /** * Clears collected code coverage data. */ public function clear(): void { $this->isInitialized = false; $this->currentId = null; $this->data = []; $this->tests = []; $this->report = null; } /** * Returns the filter object used. */ public function filter(): Filter { return $this->filter; } /** * Returns the collected code coverage data. */ public function getData(bool $raw = false): array { if (!$raw && $this->addUncoveredFilesFromWhitelist) { $this->addUncoveredFilesFromWhitelist(); } return $this->data; } /** * Sets the coverage data. */ public function setData(array $data): void { $this->data = $data; $this->report = null; } /** * Returns the test data. */ public function getTests(): array { return $this->tests; } /** * Sets the test data. */ public function setTests(array $tests): void { $this->tests = $tests; } /** * Start collection of code coverage information. * * @param PhptTestCase|string|TestCase $id * * @throws RuntimeException */ public function start($id, bool $clear = false): void { if ($clear) { $this->clear(); } if ($this->isInitialized === false) { $this->initializeData(); } $this->currentId = $id; $this->driver->start($this->shouldCheckForDeadAndUnused); } /** * Stop collection of code coverage information. * * @param array|false $linesToBeCovered * * @throws MissingCoversAnnotationException * @throws CoveredCodeNotExecutedException * @throws RuntimeException * @throws InvalidArgumentException * @throws \ReflectionException */ public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): array { if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { throw InvalidArgumentException::create( 2, 'array or false' ); } $data = $this->driver->stop(); $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation); $this->currentId = null; return $data; } /** * Appends code coverage data. * * @param PhptTestCase|string|TestCase $id * @param array|false $linesToBeCovered * * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException * @throws \ReflectionException * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException * @throws RuntimeException */ public function append(array $data, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void { if ($id === null) { $id = $this->currentId; } if ($id === null) { throw new RuntimeException; } $this->applyWhitelistFilter($data); $this->applyIgnoredLinesFilter($data); $this->initializeFilesThatAreSeenTheFirstTime($data); if (!$append) { return; } if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { $this->applyCoversAnnotationFilter( $data, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation ); } if (empty($data)) { return; } $size = 'unknown'; $status = -1; if ($id instanceof TestCase) { $_size = $id->getSize(); if ($_size === Test::SMALL) { $size = 'small'; } elseif ($_size === Test::MEDIUM) { $size = 'medium'; } elseif ($_size === Test::LARGE) { $size = 'large'; } $status = $id->getStatus(); $id = \get_class($id) . '::' . $id->getName(); } elseif ($id instanceof PhptTestCase) { $size = 'large'; $id = $id->getName(); } $this->tests[$id] = ['size' => $size, 'status' => $status]; foreach ($data as $file => $lines) { if (!$this->filter->isFile($file)) { continue; } foreach ($lines as $k => $v) { if ($v === Driver::LINE_EXECUTED) { if (empty($this->data[$file][$k]) || !\in_array($id, $this->data[$file][$k])) { $this->data[$file][$k][] = $id; } } } } $this->report = null; } /** * Merges the data from another instance. * * @param CodeCoverage $that */ public function merge(self $that): void { $this->filter->setWhitelistedFiles( \array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) ); foreach ($that->data as $file => $lines) { if (!isset($this->data[$file])) { if (!$this->filter->isFiltered($file)) { $this->data[$file] = $lines; } continue; } // we should compare the lines if any of two contains data $compareLineNumbers = \array_unique( \array_merge( \array_keys($this->data[$file]), \array_keys($that->data[$file]) ) ); foreach ($compareLineNumbers as $line) { $thatPriority = $this->getLinePriority($that->data[$file], $line); $thisPriority = $this->getLinePriority($this->data[$file], $line); if ($thatPriority > $thisPriority) { $this->data[$file][$line] = $that->data[$file][$line]; } elseif ($thatPriority === $thisPriority && \is_array($this->data[$file][$line])) { $this->data[$file][$line] = \array_unique( \array_merge($this->data[$file][$line], $that->data[$file][$line]) ); } } } $this->tests = \array_merge($this->tests, $that->getTests()); $this->report = null; } public function setCacheTokens(bool $flag): void { $this->cacheTokens = $flag; } public function getCacheTokens(): bool { return $this->cacheTokens; } public function setCheckForUnintentionallyCoveredCode(bool $flag): void { $this->checkForUnintentionallyCoveredCode = $flag; } public function setForceCoversAnnotation(bool $flag): void { $this->forceCoversAnnotation = $flag; } public function setCheckForMissingCoversAnnotation(bool $flag): void { $this->checkForMissingCoversAnnotation = $flag; } public function setCheckForUnexecutedCoveredCode(bool $flag): void { $this->checkForUnexecutedCoveredCode = $flag; } public function setAddUncoveredFilesFromWhitelist(bool $flag): void { $this->addUncoveredFilesFromWhitelist = $flag; } public function setProcessUncoveredFilesFromWhitelist(bool $flag): void { $this->processUncoveredFilesFromWhitelist = $flag; } public function setDisableIgnoredLines(bool $flag): void { $this->disableIgnoredLines = $flag; } public function setIgnoreDeprecatedCode(bool $flag): void { $this->ignoreDeprecatedCode = $flag; } public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist): void { $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; } /** * Determine the priority for a line * * 1 = the line is not set * 2 = the line has not been tested * 3 = the line is dead code * 4 = the line has been tested * * During a merge, a higher number is better. * * @param array $data * @param int $line * * @return int */ private function getLinePriority($data, $line) { if (!\array_key_exists($line, $data)) { return 1; } if (\is_array($data[$line]) && \count($data[$line]) === 0) { return 2; } if ($data[$line] === null) { return 3; } return 4; } /** * Applies the @covers annotation filtering. * * @param array|false $linesToBeCovered * * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException * @throws \ReflectionException * @throws MissingCoversAnnotationException * @throws UnintentionallyCoveredCodeException */ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void { if ($linesToBeCovered === false || ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { if ($this->checkForMissingCoversAnnotation) { throw new MissingCoversAnnotationException; } $data = []; return; } if (empty($linesToBeCovered)) { return; } if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof TestCase || (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); } if ($this->checkForUnexecutedCoveredCode) { $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); } $data = \array_intersect_key($data, $linesToBeCovered); foreach (\array_keys($data) as $filename) { $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); } } private function applyWhitelistFilter(array &$data): void { foreach (\array_keys($data) as $filename) { if ($this->filter->isFiltered($filename)) { unset($data[$filename]); } } } /** * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException */ private function applyIgnoredLinesFilter(array &$data): void { foreach (\array_keys($data) as $filename) { if (!$this->filter->isFile($filename)) { continue; } foreach ($this->getLinesToBeIgnored($filename) as $line) { unset($data[$filename][$line]); } } } private function initializeFilesThatAreSeenTheFirstTime(array $data): void { foreach ($data as $file => $lines) { if (!isset($this->data[$file]) && $this->filter->isFile($file)) { $this->data[$file] = []; foreach ($lines as $k => $v) { $this->data[$file][$k] = $v === -2 ? null : []; } } } } /** * @throws CoveredCodeNotExecutedException * @throws InvalidArgumentException * @throws MissingCoversAnnotationException * @throws RuntimeException * @throws UnintentionallyCoveredCodeException * @throws \ReflectionException */ private function addUncoveredFilesFromWhitelist(): void { $data = []; $uncoveredFiles = \array_diff( $this->filter->getWhitelist(), \array_keys($this->data) ); foreach ($uncoveredFiles as $uncoveredFile) { if (!\file_exists($uncoveredFile)) { continue; } $data[$uncoveredFile] = []; $lines = \count(\file($uncoveredFile)); for ($i = 1; $i <= $lines; $i++) { $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; } } $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); } private function getLinesToBeIgnored(string $fileName): array { if (isset($this->ignoredLines[$fileName])) { return $this->ignoredLines[$fileName]; } try { return $this->getLinesToBeIgnoredInner($fileName); } catch (\OutOfBoundsException $e) { // This can happen with PHP_Token_Stream if the file is syntactically invalid, // and probably affects a file that wasn't executed. return []; } } private function getLinesToBeIgnoredInner(string $fileName): array { $this->ignoredLines[$fileName] = []; $lines = \file($fileName); foreach ($lines as $index => $line) { if (!\trim($line)) { $this->ignoredLines[$fileName][] = $index + 1; } } if ($this->cacheTokens) { $tokens = \PHP_Token_Stream_CachingFactory::get($fileName); } else { $tokens = new \PHP_Token_Stream($fileName); } foreach ($tokens->getInterfaces() as $interface) { $interfaceStartLine = $interface['startLine']; $interfaceEndLine = $interface['endLine']; foreach (\range($interfaceStartLine, $interfaceEndLine) as $line) { $this->ignoredLines[$fileName][] = $line; } } foreach (\array_merge($tokens->getClasses(), $tokens->getTraits()) as $classOrTrait) { $classOrTraitStartLine = $classOrTrait['startLine']; $classOrTraitEndLine = $classOrTrait['endLine']; if (empty($classOrTrait['methods'])) { foreach (\range($classOrTraitStartLine, $classOrTraitEndLine) as $line) { $this->ignoredLines[$fileName][] = $line; } continue; } $firstMethod = \array_shift($classOrTrait['methods']); $firstMethodStartLine = $firstMethod['startLine']; $firstMethodEndLine = $firstMethod['endLine']; $lastMethodEndLine = $firstMethodEndLine; do { $lastMethod = \array_pop($classOrTrait['methods']); } while ($lastMethod !== null && 0 === \strpos($lastMethod['signature'], 'anonymousFunction')); if ($lastMethod !== null) { $lastMethodEndLine = $lastMethod['endLine']; } foreach (\range($classOrTraitStartLine, $firstMethodStartLine) as $line) { $this->ignoredLines[$fileName][] = $line; } foreach (\range($lastMethodEndLine + 1, $classOrTraitEndLine) as $line) { $this->ignoredLines[$fileName][] = $line; } } if ($this->disableIgnoredLines) { $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); \sort($this->ignoredLines[$fileName]); return $this->ignoredLines[$fileName]; } $ignore = false; $stop = false; foreach ($tokens->tokens() as $token) { switch (\get_class($token)) { case \PHP_Token_COMMENT::class: case \PHP_Token_DOC_COMMENT::class: $_token = \trim((string) $token); $_line = \trim($lines[$token->getLine() - 1]); if ($_token === '// @codeCoverageIgnore' || $_token === '//@codeCoverageIgnore') { $ignore = true; $stop = true; } elseif ($_token === '// @codeCoverageIgnoreStart' || $_token === '//@codeCoverageIgnoreStart') { $ignore = true; } elseif ($_token === '// @codeCoverageIgnoreEnd' || $_token === '//@codeCoverageIgnoreEnd') { $stop = true; } if (!$ignore) { $start = $token->getLine(); $end = $start + \substr_count((string) $token, "\n"); // Do not ignore the first line when there is a token // before the comment if (0 !== \strpos($_token, $_line)) { $start++; } for ($i = $start; $i < $end; $i++) { $this->ignoredLines[$fileName][] = $i; } // A DOC_COMMENT token or a COMMENT token starting with "/*" // does not contain the final \n character in its text if (isset($lines[$i - 1]) && 0 === \strpos($_token, '/*') && '*/' === \substr(\trim($lines[$i - 1]), -2)) { $this->ignoredLines[$fileName][] = $i; } } break; case \PHP_Token_INTERFACE::class: case \PHP_Token_TRAIT::class: case \PHP_Token_CLASS::class: case \PHP_Token_FUNCTION::class: /* @var \PHP_Token_Interface $token */ $docblock = (string) $token->getDocblock(); $this->ignoredLines[$fileName][] = $token->getLine(); if (\strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && \strpos($docblock, '@deprecated'))) { $endLine = $token->getEndLine(); for ($i = $token->getLine(); $i <= $endLine; $i++) { $this->ignoredLines[$fileName][] = $i; } } break; /* @noinspection PhpMissingBreakStatementInspection */ case \PHP_Token_NAMESPACE::class: $this->ignoredLines[$fileName][] = $token->getEndLine(); // Intentional fallthrough case \PHP_Token_DECLARE::class: case \PHP_Token_OPEN_TAG::class: case \PHP_Token_CLOSE_TAG::class: case \PHP_Token_USE::class: $this->ignoredLines[$fileName][] = $token->getLine(); break; } if ($ignore) { $this->ignoredLines[$fileName][] = $token->getLine(); if ($stop) { $ignore = false; $stop = false; } } } $this->ignoredLines[$fileName][] = \count($lines) + 1; $this->ignoredLines[$fileName] = \array_unique( $this->ignoredLines[$fileName] ); $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); \sort($this->ignoredLines[$fileName]); return $this->ignoredLines[$fileName]; } /** * @throws \ReflectionException * @throws UnintentionallyCoveredCodeException */ private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void { $allowedLines = $this->getAllowedLines( $linesToBeCovered, $linesToBeUsed ); $unintentionallyCoveredUnits = []; foreach ($data as $file => $_data) { foreach ($_data as $line => $flag) { if ($flag === 1 && !isset($allowedLines[$file][$line])) { $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); } } } $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); if (!empty($unintentionallyCoveredUnits)) { throw new UnintentionallyCoveredCodeException( $unintentionallyCoveredUnits ); } } /** * @throws CoveredCodeNotExecutedException */ private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void { $executedCodeUnits = $this->coverageToCodeUnits($data); $message = ''; foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { if (!\in_array($codeUnit, $executedCodeUnits)) { $message .= \sprintf( '- %s is expected to be executed (@covers) but was not executed' . "\n", $codeUnit ); } } foreach ($this->linesToCodeUnits($linesToBeUsed) as $codeUnit) { if (!\in_array($codeUnit, $executedCodeUnits)) { $message .= \sprintf( '- %s is expected to be executed (@uses) but was not executed' . "\n", $codeUnit ); } } if (!empty($message)) { throw new CoveredCodeNotExecutedException($message); } } private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array { $allowedLines = []; foreach (\array_keys($linesToBeCovered) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } $allowedLines[$file] = \array_merge( $allowedLines[$file], $linesToBeCovered[$file] ); } foreach (\array_keys($linesToBeUsed) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } $allowedLines[$file] = \array_merge( $allowedLines[$file], $linesToBeUsed[$file] ); } foreach (\array_keys($allowedLines) as $file) { $allowedLines[$file] = \array_flip( \array_unique($allowedLines[$file]) ); } return $allowedLines; } /** * @throws RuntimeException */ private function selectDriver(Filter $filter): Driver { $runtime = new Runtime; if (!$runtime->canCollectCodeCoverage()) { throw new RuntimeException('No code coverage driver available'); } if ($runtime->isPHPDBG()) { return new PHPDBG; } if ($runtime->hasXdebug()) { return new Xdebug($filter); } if ($runtime->hasPCOV()) { return new PCOV($filter); } throw new RuntimeException('No code coverage driver available'); } private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits): array { $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); \sort($unintentionallyCoveredUnits); foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { $unit = \explode('::', $unintentionallyCoveredUnits[$k]); if (\count($unit) !== 2) { continue; } $class = new \ReflectionClass($unit[0]); foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { if ($class->isSubclassOf($whitelisted)) { unset($unintentionallyCoveredUnits[$k]); break; } } } return \array_values($unintentionallyCoveredUnits); } /** * @throws CoveredCodeNotExecutedException * @throws InvalidArgumentException * @throws MissingCoversAnnotationException * @throws RuntimeException * @throws UnintentionallyCoveredCodeException * @throws \ReflectionException */ private function initializeData(): void { $this->isInitialized = true; if ($this->processUncoveredFilesFromWhitelist) { $this->shouldCheckForDeadAndUnused = false; $this->driver->start(); foreach ($this->filter->getWhitelist() as $file) { if ($this->filter->isFile($file)) { include_once $file; } } $data = []; $coverage = $this->driver->stop(); foreach ($coverage as $file => $fileCoverage) { if ($this->filter->isFiltered($file)) { continue; } foreach (\array_keys($fileCoverage) as $key) { if ($fileCoverage[$key] === Driver::LINE_EXECUTED) { $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; } } $data[$file] = $fileCoverage; } $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); } } private function coverageToCodeUnits(array $data): array { $codeUnits = []; foreach ($data as $filename => $lines) { foreach ($lines as $line => $flag) { if ($flag === 1) { $codeUnits[] = $this->wizard->lookup($filename, $line); } } } return \array_unique($codeUnits); } private function linesToCodeUnits(array $data): array { $codeUnits = []; foreach ($data as $filename => $lines) { foreach ($lines as $line) { $codeUnits[] = $this->wizard->lookup($filename, $line); } } return \array_unique($codeUnits); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage; use SebastianBergmann\Version as VersionId; final class Version { /** * @var string */ private static $version; public static function id(): string { if (self::$version === null) { $version = new VersionId('7.0.3', \dirname(__DIR__)); self::$version = $version->getVersion(); } return self::$version; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage; /** * Utility methods. */ final class Util { /** * @return float|int|string */ public static function percent(float $a, float $b, bool $asString = false, bool $fixedWidth = false) { if ($asString && $b == 0) { return ''; } $percent = 100; if ($b > 0) { $percent = ($a / $b) * 100; } if ($asString) { $format = $fixedWidth ? '%6.2F%%' : '%01.2F%%'; return \sprintf($format, $percent); } return $percent; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Node; use SebastianBergmann\CodeCoverage\Util; /** * Base class for nodes in the code coverage information tree. */ abstract class AbstractNode implements \Countable { /** * @var string */ private $name; /** * @var string */ private $path; /** * @var array */ private $pathArray; /** * @var AbstractNode */ private $parent; /** * @var string */ private $id; public function __construct(string $name, self $parent = null) { if (\substr($name, -1) == \DIRECTORY_SEPARATOR) { $name = \substr($name, 0, -1); } $this->name = $name; $this->parent = $parent; } public function getName(): string { return $this->name; } public function getId(): string { 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; } public function getPath(): string { if ($this->path === null) { if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) { $this->path = $this->name; } else { $this->path = $this->parent->getPath() . \DIRECTORY_SEPARATOR . $this->name; } } return $this->path; } public function getPathAsArray(): array { if ($this->pathArray === null) { if ($this->parent === null) { $this->pathArray = []; } else { $this->pathArray = $this->parent->getPathAsArray(); } $this->pathArray[] = $this; } return $this->pathArray; } public function getParent(): ?self { return $this->parent; } /** * Returns the percentage of classes that has been tested. * * @return int|string */ public function getTestedClassesPercent(bool $asString = true) { return Util::percent( $this->getNumTestedClasses(), $this->getNumClasses(), $asString ); } /** * Returns the percentage of traits that has been tested. * * @return int|string */ public function getTestedTraitsPercent(bool $asString = true) { return Util::percent( $this->getNumTestedTraits(), $this->getNumTraits(), $asString ); } /** * Returns the percentage of classes and traits that has been tested. * * @return int|string */ public function getTestedClassesAndTraitsPercent(bool $asString = true) { return Util::percent( $this->getNumTestedClassesAndTraits(), $this->getNumClassesAndTraits(), $asString ); } /** * Returns the percentage of functions that has been tested. * * @return int|string */ public function getTestedFunctionsPercent(bool $asString = true) { return Util::percent( $this->getNumTestedFunctions(), $this->getNumFunctions(), $asString ); } /** * Returns the percentage of methods that has been tested. * * @return int|string */ public function getTestedMethodsPercent(bool $asString = true) { return Util::percent( $this->getNumTestedMethods(), $this->getNumMethods(), $asString ); } /** * Returns the percentage of functions and methods that has been tested. * * @return int|string */ public function getTestedFunctionsAndMethodsPercent(bool $asString = true) { return Util::percent( $this->getNumTestedFunctionsAndMethods(), $this->getNumFunctionsAndMethods(), $asString ); } /** * Returns the percentage of executed lines. * * @return int|string */ public function getLineExecutedPercent(bool $asString = true) { return Util::percent( $this->getNumExecutedLines(), $this->getNumExecutableLines(), $asString ); } /** * Returns the number of classes and traits. */ public function getNumClassesAndTraits(): int { return $this->getNumClasses() + $this->getNumTraits(); } /** * Returns the number of tested classes and traits. */ public function getNumTestedClassesAndTraits(): int { return $this->getNumTestedClasses() + $this->getNumTestedTraits(); } /** * Returns the classes and traits of this node. */ public function getClassesAndTraits(): array { return \array_merge($this->getClasses(), $this->getTraits()); } /** * Returns the number of functions and methods. */ public function getNumFunctionsAndMethods(): int { return $this->getNumFunctions() + $this->getNumMethods(); } /** * Returns the number of tested functions and methods. */ public function getNumTestedFunctionsAndMethods(): int { return $this->getNumTestedFunctions() + $this->getNumTestedMethods(); } /** * Returns the functions and methods of this node. */ public function getFunctionsAndMethods(): array { return \array_merge($this->getFunctions(), $this->getMethods()); } /** * Returns the classes of this node. */ abstract public function getClasses(): array; /** * Returns the traits of this node. */ abstract public function getTraits(): array; /** * Returns the functions of this node. */ abstract public function getFunctions(): array; /** * Returns the LOC/CLOC/NCLOC of this node. */ abstract public function getLinesOfCode(): array; /** * Returns the number of executable lines. */ abstract public function getNumExecutableLines(): int; /** * Returns the number of executed lines. */ abstract public function getNumExecutedLines(): int; /** * Returns the number of classes. */ abstract public function getNumClasses(): int; /** * Returns the number of tested classes. */ abstract public function getNumTestedClasses(): int; /** * Returns the number of traits. */ abstract public function getNumTraits(): int; /** * Returns the number of tested traits. */ abstract public function getNumTestedTraits(): int; /** * Returns the number of methods. */ abstract public function getNumMethods(): int; /** * Returns the number of tested methods. */ abstract public function getNumTestedMethods(): int; /** * Returns the number of functions. */ abstract public function getNumFunctions(): int; /** * Returns the number of tested functions. */ abstract public function getNumTestedFunctions(): int; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Node; /** * Recursive iterator for node object graphs. */ final class Iterator implements \RecursiveIterator { /** * @var int */ private $position; /** * @var AbstractNode[] */ private $nodes; public function __construct(Directory $node) { $this->nodes = $node->getChildNodes(); } /** * Rewinds the Iterator to the first element. */ public function rewind(): void { $this->position = 0; } /** * Checks if there is a current element after calls to rewind() or next(). */ public function valid(): bool { return $this->position < \count($this->nodes); } /** * Returns the key of the current element. */ public function key(): int { return $this->position; } /** * Returns the current element. */ public function current(): AbstractNode { return $this->valid() ? $this->nodes[$this->position] : null; } /** * Moves forward to next element. */ public function next(): void { $this->position++; } /** * Returns the sub iterator for the current element. * * @return Iterator */ public function getChildren(): self { return new self($this->nodes[$this->position]); } /** * Checks whether the current element has children. */ public function hasChildren(): bool { return $this->nodes[$this->position] instanceof Directory; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Node; use SebastianBergmann\CodeCoverage\CodeCoverage; final class Builder { public function build(CodeCoverage $coverage): Directory { $files = $coverage->getData(); $commonPath = $this->reducePaths($files); $root = new Directory( $commonPath, null ); $this->addItems( $root, $this->buildDirectoryStructure($files), $coverage->getTests(), $coverage->getCacheTokens() ); return $root; } private function addItems(Directory $root, array $items, array $tests, bool $cacheTokens): void { foreach ($items as $key => $value) { $key = (string) $key; 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 * ( * ... * ) * ) * ) * */ private function buildDirectoryStructure(array $files): array { $result = []; foreach ($files as $path => $file) { $path = \explode(\DIRECTORY_SEPARATOR, $path); $pointer = &$result; $max = \count($path); for ($i = 0; $i < $max; $i++) { $type = ''; if ($i === ($max - 1)) { $type = '/f'; } $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 * ( * ... * ) * ) * */ private function reducePaths(array &$files): string { if (empty($files)) { return '.'; } $commonPath = ''; $paths = \array_keys($files); if (\count($files) === 1) { $commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR; $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] = \str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]); } $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[\implode(\DIRECTORY_SEPARATOR, $paths[$i])] = $files[$original[$i]]; unset($files[$original[$i]]); } \ksort($files); return \substr($commonPath, 0, -1); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Node; use SebastianBergmann\CodeCoverage\InvalidArgumentException; /** * Represents a directory in the code coverage information tree. */ final class Directory extends AbstractNode implements \IteratorAggregate { /** * @var AbstractNode[] */ private $children = []; /** * @var Directory[] */ private $directories = []; /** * @var File[] */ private $files = []; /** * @var array */ private $classes; /** * @var array */ private $traits; /** * @var array */ private $functions; /** * @var array */ private $linesOfCode; /** * @var int */ private $numFiles = -1; /** * @var int */ private $numExecutableLines = -1; /** * @var int */ private $numExecutedLines = -1; /** * @var int */ private $numClasses = -1; /** * @var int */ private $numTestedClasses = -1; /** * @var int */ private $numTraits = -1; /** * @var int */ private $numTestedTraits = -1; /** * @var int */ private $numMethods = -1; /** * @var int */ private $numTestedMethods = -1; /** * @var int */ private $numFunctions = -1; /** * @var int */ private $numTestedFunctions = -1; /** * Returns the number of files in/under this node. */ public function count(): int { 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. */ public function getIterator(): \RecursiveIteratorIterator { return new \RecursiveIteratorIterator( new Iterator($this), \RecursiveIteratorIterator::SELF_FIRST ); } /** * Adds a new directory. */ public function addDirectory(string $name): self { $directory = new self($name, $this); $this->children[] = $directory; $this->directories[] = &$this->children[\count($this->children) - 1]; return $directory; } /** * Adds a new file. * * @throws InvalidArgumentException */ public function addFile(string $name, array $coverageData, array $testData, bool $cacheTokens): File { $file = new File($name, $this, $coverageData, $testData, $cacheTokens); $this->children[] = $file; $this->files[] = &$this->children[\count($this->children) - 1]; $this->numExecutableLines = -1; $this->numExecutedLines = -1; return $file; } /** * Returns the directories in this directory. */ public function getDirectories(): array { return $this->directories; } /** * Returns the files in this directory. */ public function getFiles(): array { return $this->files; } /** * Returns the child nodes of this node. */ public function getChildNodes(): array { return $this->children; } /** * Returns the classes of this node. */ public function getClasses(): array { if ($this->classes === null) { $this->classes = []; foreach ($this->children as $child) { $this->classes = \array_merge( $this->classes, $child->getClasses() ); } } return $this->classes; } /** * Returns the traits of this node. */ public function getTraits(): array { if ($this->traits === null) { $this->traits = []; foreach ($this->children as $child) { $this->traits = \array_merge( $this->traits, $child->getTraits() ); } } return $this->traits; } /** * Returns the functions of this node. */ public function getFunctions(): array { if ($this->functions === null) { $this->functions = []; foreach ($this->children as $child) { $this->functions = \array_merge( $this->functions, $child->getFunctions() ); } } return $this->functions; } /** * Returns the LOC/CLOC/NCLOC of this node. */ public function getLinesOfCode(): array { if ($this->linesOfCode === null) { $this->linesOfCode = ['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. */ public function getNumExecutableLines(): int { if ($this->numExecutableLines === -1) { $this->numExecutableLines = 0; foreach ($this->children as $child) { $this->numExecutableLines += $child->getNumExecutableLines(); } } return $this->numExecutableLines; } /** * Returns the number of executed lines. */ public function getNumExecutedLines(): int { if ($this->numExecutedLines === -1) { $this->numExecutedLines = 0; foreach ($this->children as $child) { $this->numExecutedLines += $child->getNumExecutedLines(); } } return $this->numExecutedLines; } /** * Returns the number of classes. */ public function getNumClasses(): int { if ($this->numClasses === -1) { $this->numClasses = 0; foreach ($this->children as $child) { $this->numClasses += $child->getNumClasses(); } } return $this->numClasses; } /** * Returns the number of tested classes. */ public function getNumTestedClasses(): int { if ($this->numTestedClasses === -1) { $this->numTestedClasses = 0; foreach ($this->children as $child) { $this->numTestedClasses += $child->getNumTestedClasses(); } } return $this->numTestedClasses; } /** * Returns the number of traits. */ public function getNumTraits(): int { if ($this->numTraits === -1) { $this->numTraits = 0; foreach ($this->children as $child) { $this->numTraits += $child->getNumTraits(); } } return $this->numTraits; } /** * Returns the number of tested traits. */ public function getNumTestedTraits(): int { if ($this->numTestedTraits === -1) { $this->numTestedTraits = 0; foreach ($this->children as $child) { $this->numTestedTraits += $child->getNumTestedTraits(); } } return $this->numTestedTraits; } /** * Returns the number of methods. */ public function getNumMethods(): int { if ($this->numMethods === -1) { $this->numMethods = 0; foreach ($this->children as $child) { $this->numMethods += $child->getNumMethods(); } } return $this->numMethods; } /** * Returns the number of tested methods. */ public function getNumTestedMethods(): int { if ($this->numTestedMethods === -1) { $this->numTestedMethods = 0; foreach ($this->children as $child) { $this->numTestedMethods += $child->getNumTestedMethods(); } } return $this->numTestedMethods; } /** * Returns the number of functions. */ public function getNumFunctions(): int { if ($this->numFunctions === -1) { $this->numFunctions = 0; foreach ($this->children as $child) { $this->numFunctions += $child->getNumFunctions(); } } return $this->numFunctions; } /** * Returns the number of tested functions. */ public function getNumTestedFunctions(): int { if ($this->numTestedFunctions === -1) { $this->numTestedFunctions = 0; foreach ($this->children as $child) { $this->numTestedFunctions += $child->getNumTestedFunctions(); } } return $this->numTestedFunctions; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Node; /** * Represents a file in the code coverage information tree. */ final class File extends AbstractNode { /** * @var array */ private $coverageData; /** * @var array */ private $testData; /** * @var int */ private $numExecutableLines = 0; /** * @var int */ private $numExecutedLines = 0; /** * @var array */ private $classes = []; /** * @var array */ private $traits = []; /** * @var array */ private $functions = []; /** * @var array */ private $linesOfCode = []; /** * @var int */ private $numClasses; /** * @var int */ private $numTestedClasses = 0; /** * @var int */ private $numTraits; /** * @var int */ private $numTestedTraits = 0; /** * @var int */ private $numMethods; /** * @var int */ private $numTestedMethods; /** * @var int */ private $numTestedFunctions; /** * @var bool */ private $cacheTokens; /** * @var array */ private $codeUnitsByLine = []; public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens) { parent::__construct($name, $parent); $this->coverageData = $coverageData; $this->testData = $testData; $this->cacheTokens = $cacheTokens; $this->calculateStatistics(); } /** * Returns the number of files in/under this node. */ public function count(): int { return 1; } /** * Returns the code coverage data of this node. */ public function getCoverageData(): array { return $this->coverageData; } /** * Returns the test data of this node. */ public function getTestData(): array { return $this->testData; } /** * Returns the classes of this node. */ public function getClasses(): array { return $this->classes; } /** * Returns the traits of this node. */ public function getTraits(): array { return $this->traits; } /** * Returns the functions of this node. */ public function getFunctions(): array { return $this->functions; } /** * Returns the LOC/CLOC/NCLOC of this node. */ public function getLinesOfCode(): array { return $this->linesOfCode; } /** * Returns the number of executable lines. */ public function getNumExecutableLines(): int { return $this->numExecutableLines; } /** * Returns the number of executed lines. */ public function getNumExecutedLines(): int { return $this->numExecutedLines; } /** * Returns the number of classes. */ public function getNumClasses(): int { if ($this->numClasses === null) { $this->numClasses = 0; foreach ($this->classes as $class) { foreach ($class['methods'] as $method) { if ($method['executableLines'] > 0) { $this->numClasses++; continue 2; } } } } return $this->numClasses; } /** * Returns the number of tested classes. */ public function getNumTestedClasses(): int { return $this->numTestedClasses; } /** * Returns the number of traits. */ public function getNumTraits(): int { if ($this->numTraits === null) { $this->numTraits = 0; foreach ($this->traits as $trait) { foreach ($trait['methods'] as $method) { if ($method['executableLines'] > 0) { $this->numTraits++; continue 2; } } } } return $this->numTraits; } /** * Returns the number of tested traits. */ public function getNumTestedTraits(): int { return $this->numTestedTraits; } /** * Returns the number of methods. */ public function getNumMethods(): int { 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. */ public function getNumTestedMethods(): int { 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. */ public function getNumFunctions(): int { return \count($this->functions); } /** * Returns the number of tested functions. */ public function getNumTestedFunctions(): int { if ($this->numTestedFunctions === null) { $this->numTestedFunctions = 0; foreach ($this->functions as $function) { if ($function['executableLines'] > 0 && $function['coverage'] === 100) { $this->numTestedFunctions++; } } } return $this->numTestedFunctions; } private function calculateStatistics(): void { if ($this->cacheTokens) { $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); } else { $tokens = new \PHP_Token_Stream($this->getPath()); } $this->linesOfCode = $tokens->getLinesOfCode(); foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = []; } try { $this->processClasses($tokens); $this->processTraits($tokens); $this->processFunctions($tokens); } catch (\OutOfBoundsException $e) { // This can happen with PHP_Token_Stream if the file is syntactically invalid, // and probably affects a file that wasn't executed. } unset($tokens); foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { if (isset($this->coverageData[$lineNumber])) { foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { $codeUnit['executableLines']++; } unset($codeUnit); $this->numExecutableLines++; if (\count($this->coverageData[$lineNumber]) > 0) { foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { $codeUnit['executedLines']++; } unset($codeUnit); $this->numExecutedLines++; } } } 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']; } unset($method); if ($trait['executableLines'] > 0) { $trait['coverage'] = ($trait['executedLines'] / $trait['executableLines']) * 100; if ($trait['coverage'] === 100) { $this->numTestedClasses++; } } else { $trait['coverage'] = 100; } $trait['crap'] = $this->crap( $trait['ccn'], $trait['coverage'] ); } unset($trait); 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']; } unset($method); if ($class['executableLines'] > 0) { $class['coverage'] = ($class['executedLines'] / $class['executableLines']) * 100; if ($class['coverage'] === 100) { $this->numTestedClasses++; } } else { $class['coverage'] = 100; } $class['crap'] = $this->crap( $class['ccn'], $class['coverage'] ); } unset($class); foreach ($this->functions as &$function) { if ($function['executableLines'] > 0) { $function['coverage'] = ($function['executedLines'] / $function['executableLines']) * 100; } else { $function['coverage'] = 100; } if ($function['coverage'] === 100) { $this->numTestedFunctions++; } $function['crap'] = $this->crap( $function['ccn'], $function['coverage'] ); } } private function processClasses(\PHP_Token_Stream $tokens): void { $classes = $tokens->getClasses(); $link = $this->getId() . '.html#'; foreach ($classes as $className => $class) { if (\strpos($className, 'anonymous') === 0) { continue; } if (!empty($class['package']['namespace'])) { $className = $class['package']['namespace'] . '\\' . $className; } $this->classes[$className] = [ 'className' => $className, 'methods' => [], 'startLine' => $class['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'package' => $class['package'], 'link' => $link . $class['startLine'], ]; foreach ($class['methods'] as $methodName => $method) { if (\strpos($methodName, 'anonymous') === 0) { continue; } $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = [ &$this->classes[$className], &$this->classes[$className]['methods'][$methodName], ]; } } } } private function processTraits(\PHP_Token_Stream $tokens): void { $traits = $tokens->getTraits(); $link = $this->getId() . '.html#'; foreach ($traits as $traitName => $trait) { $this->traits[$traitName] = [ 'traitName' => $traitName, 'methods' => [], 'startLine' => $trait['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'package' => $trait['package'], 'link' => $link . $trait['startLine'], ]; foreach ($trait['methods'] as $methodName => $method) { if (\strpos($methodName, 'anonymous') === 0) { continue; } $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = [ &$this->traits[$traitName], &$this->traits[$traitName]['methods'][$methodName], ]; } } } } private function processFunctions(\PHP_Token_Stream $tokens): void { $functions = $tokens->getFunctions(); $link = $this->getId() . '.html#'; foreach ($functions as $functionName => $function) { if (\strpos($functionName, 'anonymous') === 0) { continue; } $this->functions[$functionName] = [ 'functionName' => $functionName, 'signature' => $function['signature'], 'startLine' => $function['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => $function['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $function['startLine'], ]; foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; } } } private function crap(int $ccn, float $coverage): string { if ($coverage === 0) { return (string) ($ccn ** 2 + $ccn); } if ($coverage >= 95) { return (string) $ccn; } return \sprintf( '%01.2F', $ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn ); } private function newMethod(string $methodName, array $method, string $link): array { return [ 'methodName' => $methodName, 'visibility' => $method['visibility'], 'signature' => $method['signature'], 'startLine' => $method['startLine'], 'endLine' => $method['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => $method['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $method['startLine'], ]; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; final class Project extends Node { public function __construct(string $directory) { $this->init(); $this->setProjectSourceDirectory($directory); } public function getProjectSourceDirectory(): string { return $this->getContextNode()->getAttribute('source'); } public function getBuildInformation(): BuildInformation { $buildNode = $this->getDom()->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'build' )->item(0); if (!$buildNode) { $buildNode = $this->getDom()->documentElement->appendChild( $this->getDom()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'build' ) ); } return new BuildInformation($buildNode); } public function getTests(): Tests { $testsNode = $this->getContextNode()->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'tests' )->item(0); if (!$testsNode) { $testsNode = $this->getContextNode()->appendChild( $this->getDom()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'tests' ) ); } return new Tests($testsNode); } public function asDom(): \DOMDocument { return $this->getDom(); } private function init(): void { $dom = new \DOMDocument; $dom->loadXML(''); $this->setContextNode( $dom->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'project' )->item(0) ); } private function setProjectSourceDirectory(string $name): void { $this->getContextNode()->setAttribute('source', $name); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; use TheSeer\Tokenizer\NamespaceUri; use TheSeer\Tokenizer\Tokenizer; use TheSeer\Tokenizer\XMLSerializer; final class Source { /** @var \DOMElement */ private $context; public function __construct(\DOMElement $context) { $this->context = $context; } public function setSourceCode(string $source): void { $context = $this->context; $tokens = (new Tokenizer())->parse($source); $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens); $context->parentNode->replaceChild( $context->ownerDocument->importNode($srcDom->documentElement, true), $context ); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; final class Report extends File { public function __construct(string $name) { $dom = new \DOMDocument(); $dom->loadXML(''); $contextNode = $dom->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'file' )->item(0); parent::__construct($contextNode); $this->setName($name); } public function asDom(): \DOMDocument { return $this->getDomDocument(); } public function getFunctionObject($name): Method { $node = $this->getContextNode()->appendChild( $this->getDomDocument()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'function' ) ); return new Method($node, $name); } public function getClassObject($name): Unit { return $this->getUnitObject('class', $name); } public function getTraitObject($name): Unit { return $this->getUnitObject('trait', $name); } public function getSource(): Source { $source = $this->getContextNode()->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'source' )->item(0); if (!$source) { $source = $this->getContextNode()->appendChild( $this->getDomDocument()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'source' ) ); } return new Source($source); } private function setName($name): void { $this->getContextNode()->setAttribute('name', \basename($name)); $this->getContextNode()->setAttribute('path', \dirname($name)); } private function getUnitObject($tagName, $name): Unit { $node = $this->getContextNode()->appendChild( $this->getDomDocument()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', $tagName ) ); return new Unit($node, $name); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; final class Unit { /** * @var \DOMElement */ private $contextNode; public function __construct(\DOMElement $context, string $name) { $this->contextNode = $context; $this->setName($name); } public function setLines(int $start, int $executable, int $executed): void { $this->contextNode->setAttribute('start', (string) $start); $this->contextNode->setAttribute('executable', (string) $executable); $this->contextNode->setAttribute('executed', (string) $executed); } public function setCrap(float $crap): void { $this->contextNode->setAttribute('crap', (string) $crap); } public function setPackage(string $full, string $package, string $sub, string $category): void { $node = $this->contextNode->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'package' )->item(0); if (!$node) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'package' ) ); } $node->setAttribute('full', $full); $node->setAttribute('name', $package); $node->setAttribute('sub', $sub); $node->setAttribute('category', $category); } public function setNamespace(string $namespace): void { $node = $this->contextNode->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'namespace' )->item(0); if (!$node) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'namespace' ) ); } $node->setAttribute('name', $namespace); } public function addMethod(string $name): Method { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'method' ) ); return new Method($node, $name); } private function setName(string $name): void { $this->contextNode->setAttribute('name', $name); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; abstract class Node { /** * @var \DOMDocument */ private $dom; /** * @var \DOMElement */ private $contextNode; public function __construct(\DOMElement $context) { $this->setContextNode($context); } public function getDom(): \DOMDocument { return $this->dom; } public function getTotals(): Totals { $totalsContainer = $this->getContextNode()->firstChild; if (!$totalsContainer) { $totalsContainer = $this->getContextNode()->appendChild( $this->dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'totals' ) ); } return new Totals($totalsContainer); } public function addDirectory(string $name): Directory { $dirNode = $this->getDom()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'directory' ); $dirNode->setAttribute('name', $name); $this->getContextNode()->appendChild($dirNode); return new Directory($dirNode); } public function addFile(string $name, string $href): File { $fileNode = $this->getDom()->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'file' ); $fileNode->setAttribute('name', $name); $fileNode->setAttribute('href', $href); $this->getContextNode()->appendChild($fileNode); return new File($fileNode); } protected function setContextNode(\DOMElement $context): void { $this->dom = $context->ownerDocument; $this->contextNode = $context; } protected function getContextNode(): \DOMElement { return $this->contextNode; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; final class Directory extends Node { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; use SebastianBergmann\CodeCoverage\RuntimeException; final class Coverage { /** * @var \XMLWriter */ private $writer; /** * @var \DOMElement */ private $contextNode; /** * @var bool */ private $finalized = false; public function __construct(\DOMElement $context, string $line) { $this->contextNode = $context; $this->writer = new \XMLWriter(); $this->writer->openMemory(); $this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); $this->writer->writeAttribute('nr', $line); } /** * @throws RuntimeException */ public function addTest(string $test): void { if ($this->finalized) { throw new RuntimeException('Coverage Report already finalized'); } $this->writer->startElement('covered'); $this->writer->writeAttribute('by', $test); $this->writer->endElement(); } public function finalize(): void { $this->writer->endElement(); $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); $fragment->appendXML($this->writer->outputMemory()); $this->contextNode->parentNode->replaceChild( $fragment, $this->contextNode ); $this->finalized = true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; final class Method { /** * @var \DOMElement */ private $contextNode; public function __construct(\DOMElement $context, string $name) { $this->contextNode = $context; $this->setName($name); } public function setSignature(string $signature): void { $this->contextNode->setAttribute('signature', $signature); } public function setLines(string $start, ?string $end = null): void { $this->contextNode->setAttribute('start', $start); if ($end !== null) { $this->contextNode->setAttribute('end', $end); } } public function setTotals(string $executable, string $executed, string $coverage): void { $this->contextNode->setAttribute('executable', $executable); $this->contextNode->setAttribute('executed', $executed); $this->contextNode->setAttribute('coverage', $coverage); } public function setCrap(string $crap): void { $this->contextNode->setAttribute('crap', $crap); } private function setName(string $name): void { $this->contextNode->setAttribute('name', $name); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; final class Tests { private $contextNode; private $codeMap = [ -1 => 'UNKNOWN', // PHPUnit_Runner_BaseTestRunner::STATUS_UNKNOWN 0 => 'PASSED', // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED 1 => 'SKIPPED', // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED 2 => 'INCOMPLETE', // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE 3 => 'FAILURE', // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE 4 => 'ERROR', // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR 5 => 'RISKY', // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY 6 => 'WARNING', // PHPUnit_Runner_BaseTestRunner::STATUS_WARNING ]; public function __construct(\DOMElement $context) { $this->contextNode = $context; } public function addTest(string $test, array $result): void { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'test' ) ); $node->setAttribute('name', $test); $node->setAttribute('size', $result['size']); $node->setAttribute('result', (string) $result['status']); $node->setAttribute('status', $this->codeMap[(int) $result['status']]); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Node\AbstractNode; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; use SebastianBergmann\CodeCoverage\Node\File as FileNode; use SebastianBergmann\CodeCoverage\RuntimeException; use SebastianBergmann\CodeCoverage\Version; use SebastianBergmann\Environment\Runtime; final class Facade { /** * @var string */ private $target; /** * @var Project */ private $project; /** * @var string */ private $phpUnitVersion; public function __construct(string $version) { $this->phpUnitVersion = $version; } /** * @throws RuntimeException */ public function process(CodeCoverage $coverage, string $target): void { if (\substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) { $target .= \DIRECTORY_SEPARATOR; } $this->target = $target; $this->initTargetDirectory($target); $report = $coverage->getReport(); $this->project = new Project( $coverage->getReport()->getName() ); $this->setBuildInformation(); $this->processTests($coverage->getTests()); $this->processDirectory($report, $this->project); $this->saveDocument($this->project->asDom(), 'index'); } private function setBuildInformation(): void { $buildNode = $this->project->getBuildInformation(); $buildNode->setRuntimeInformation(new Runtime()); $buildNode->setBuildTime(\DateTime::createFromFormat('U', (string) $_SERVER['REQUEST_TIME'])); $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); } /** * @throws RuntimeException */ private function initTargetDirectory(string $directory): void { if (\file_exists($directory)) { if (!\is_dir($directory)) { throw new RuntimeException( "'$directory' exists but is not a directory." ); } if (!\is_writable($directory)) { throw new RuntimeException( "'$directory' exists but is not writable." ); } } elseif (!$this->createDirectory($directory)) { throw new RuntimeException( "'$directory' could not be created." ); } } private function processDirectory(DirectoryNode $directory, Node $context): void { $directoryName = $directory->getName(); if ($this->project->getProjectSourceDirectory() === $directoryName) { $directoryName = '/'; } $directoryObject = $context->addDirectory($directoryName); $this->setTotals($directory, $directoryObject->getTotals()); foreach ($directory->getDirectories() as $node) { $this->processDirectory($node, $directoryObject); } foreach ($directory->getFiles() as $node) { $this->processFile($node, $directoryObject); } } /** * @throws RuntimeException */ private function processFile(FileNode $file, Directory $context): void { $fileObject = $context->addFile( $file->getName(), $file->getId() . '.xml' ); $this->setTotals($file, $fileObject->getTotals()); $path = \substr( $file->getPath(), \strlen($this->project->getProjectSourceDirectory()) ); $fileReport = new Report($path); $this->setTotals($file, $fileReport->getTotals()); foreach ($file->getClassesAndTraits() as $unit) { $this->processUnit($unit, $fileReport); } foreach ($file->getFunctions() as $function) { $this->processFunction($function, $fileReport); } foreach ($file->getCoverageData() as $line => $tests) { if (!\is_array($tests) || \count($tests) === 0) { continue; } $coverage = $fileReport->getLineCoverage((string) $line); foreach ($tests as $test) { $coverage->addTest($test); } $coverage->finalize(); } $fileReport->getSource()->setSourceCode( \file_get_contents($file->getPath()) ); $this->saveDocument($fileReport->asDom(), $file->getId()); } private function processUnit(array $unit, Report $report): void { if (isset($unit['className'])) { $unitObject = $report->getClassObject($unit['className']); } else { $unitObject = $report->getTraitObject($unit['traitName']); } $unitObject->setLines( $unit['startLine'], $unit['executableLines'], $unit['executedLines'] ); $unitObject->setCrap((float) $unit['crap']); $unitObject->setPackage( $unit['package']['fullPackage'], $unit['package']['package'], $unit['package']['subpackage'], $unit['package']['category'] ); $unitObject->setNamespace($unit['package']['namespace']); foreach ($unit['methods'] as $method) { $methodObject = $unitObject->addMethod($method['methodName']); $methodObject->setSignature($method['signature']); $methodObject->setLines((string) $method['startLine'], (string) $method['endLine']); $methodObject->setCrap($method['crap']); $methodObject->setTotals( (string) $method['executableLines'], (string) $method['executedLines'], (string) $method['coverage'] ); } } private function processFunction(array $function, Report $report): void { $functionObject = $report->getFunctionObject($function['functionName']); $functionObject->setSignature($function['signature']); $functionObject->setLines((string) $function['startLine']); $functionObject->setCrap($function['crap']); $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']); } private function processTests(array $tests): void { $testsObject = $this->project->getTests(); foreach ($tests as $test => $result) { if ($test === 'UNCOVERED_FILES_FROM_WHITELIST') { continue; } $testsObject->addTest($test, $result); } } private function setTotals(AbstractNode $node, Totals $totals): void { $loc = $node->getLinesOfCode(); $totals->setNumLines( $loc['loc'], $loc['cloc'], $loc['ncloc'], $node->getNumExecutableLines(), $node->getNumExecutedLines() ); $totals->setNumClasses( $node->getNumClasses(), $node->getNumTestedClasses() ); $totals->setNumTraits( $node->getNumTraits(), $node->getNumTestedTraits() ); $totals->setNumMethods( $node->getNumMethods(), $node->getNumTestedMethods() ); $totals->setNumFunctions( $node->getNumFunctions(), $node->getNumTestedFunctions() ); } private function getTargetDirectory(): string { return $this->target; } /** * @throws RuntimeException */ private function saveDocument(\DOMDocument $document, string $name): void { $filename = \sprintf('%s/%s.xml', $this->getTargetDirectory(), $name); $document->formatOutput = true; $document->preserveWhiteSpace = false; $this->initTargetDirectory(\dirname($filename)); $document->save($filename); } private function createDirectory(string $directory): bool { return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; use SebastianBergmann\CodeCoverage\Util; final class Totals { /** * @var \DOMNode */ private $container; /** * @var \DOMElement */ private $linesNode; /** * @var \DOMElement */ private $methodsNode; /** * @var \DOMElement */ private $functionsNode; /** * @var \DOMElement */ private $classesNode; /** * @var \DOMElement */ private $traitsNode; public function __construct(\DOMElement $container) { $this->container = $container; $dom = $container->ownerDocument; $this->linesNode = $dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'lines' ); $this->methodsNode = $dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'methods' ); $this->functionsNode = $dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'functions' ); $this->classesNode = $dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'classes' ); $this->traitsNode = $dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'traits' ); $container->appendChild($this->linesNode); $container->appendChild($this->methodsNode); $container->appendChild($this->functionsNode); $container->appendChild($this->classesNode); $container->appendChild($this->traitsNode); } public function getContainer(): \DOMNode { return $this->container; } public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void { $this->linesNode->setAttribute('total', (string) $loc); $this->linesNode->setAttribute('comments', (string) $cloc); $this->linesNode->setAttribute('code', (string) $ncloc); $this->linesNode->setAttribute('executable', (string) $executable); $this->linesNode->setAttribute('executed', (string) $executed); $this->linesNode->setAttribute( 'percent', $executable === 0 ? '0' : \sprintf('%01.2F', Util::percent($executed, $executable)) ); } public function setNumClasses(int $count, int $tested): void { $this->classesNode->setAttribute('count', (string) $count); $this->classesNode->setAttribute('tested', (string) $tested); $this->classesNode->setAttribute( 'percent', $count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count)) ); } public function setNumTraits(int $count, int $tested): void { $this->traitsNode->setAttribute('count', (string) $count); $this->traitsNode->setAttribute('tested', (string) $tested); $this->traitsNode->setAttribute( 'percent', $count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count)) ); } public function setNumMethods(int $count, int $tested): void { $this->methodsNode->setAttribute('count', (string) $count); $this->methodsNode->setAttribute('tested', (string) $tested); $this->methodsNode->setAttribute( 'percent', $count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count)) ); } public function setNumFunctions(int $count, int $tested): void { $this->functionsNode->setAttribute('count', (string) $count); $this->functionsNode->setAttribute('tested', (string) $tested); $this->functionsNode->setAttribute( 'percent', $count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count)) ); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; use SebastianBergmann\Environment\Runtime; final class BuildInformation { /** * @var \DOMElement */ private $contextNode; public function __construct(\DOMElement $contextNode) { $this->contextNode = $contextNode; } public function setRuntimeInformation(Runtime $runtime): void { $runtimeNode = $this->getNodeByName('runtime'); $runtimeNode->setAttribute('name', $runtime->getName()); $runtimeNode->setAttribute('version', $runtime->getVersion()); $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); $driverNode = $this->getNodeByName('driver'); if ($runtime->hasPHPDBGCodeCoverage()) { $driverNode->setAttribute('name', 'phpdbg'); $driverNode->setAttribute('version', \constant('PHPDBG_VERSION')); } if ($runtime->hasXdebug()) { $driverNode->setAttribute('name', 'xdebug'); $driverNode->setAttribute('version', \phpversion('xdebug')); } if ($runtime->hasPCOV()) { $driverNode->setAttribute('name', 'pcov'); $driverNode->setAttribute('version', \phpversion('pcov')); } } public function setBuildTime(\DateTime $date): void { $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); } public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void { $this->contextNode->setAttribute('phpunit', $phpUnitVersion); $this->contextNode->setAttribute('coverage', $coverageVersion); } private function getNodeByName(string $name): \DOMElement { $node = $this->contextNode->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', $name )->item(0); if (!$node) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'https://schema.phpunit.de/coverage/1.0', $name ) ); } return $node; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Xml; class File { /** * @var \DOMDocument */ private $dom; /** * @var \DOMElement */ private $contextNode; public function __construct(\DOMElement $context) { $this->dom = $context->ownerDocument; $this->contextNode = $context; } public function getTotals(): Totals { $totalsContainer = $this->contextNode->firstChild; if (!$totalsContainer) { $totalsContainer = $this->contextNode->appendChild( $this->dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'totals' ) ); } return new Totals($totalsContainer); } public function getLineCoverage(string $line): Coverage { $coverage = $this->contextNode->getElementsByTagNameNS( 'https://schema.phpunit.de/coverage/1.0', 'coverage' )->item(0); if (!$coverage) { $coverage = $this->contextNode->appendChild( $this->dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'coverage' ) ); } $lineNode = $coverage->appendChild( $this->dom->createElementNS( 'https://schema.phpunit.de/coverage/1.0', 'line' ) ); return new Coverage($lineNode, $line); } protected function getContextNode(): \DOMElement { return $this->contextNode; } protected function getDomDocument(): \DOMDocument { return $this->dom; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Html; use SebastianBergmann\CodeCoverage\Node\AbstractNode; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; use SebastianBergmann\CodeCoverage\Node\File as FileNode; use SebastianBergmann\CodeCoverage\Version; use SebastianBergmann\Environment\Runtime; /** * Base class for node renderers. */ abstract class Renderer { /** * @var string */ protected $templatePath; /** * @var string */ protected $generator; /** * @var string */ protected $date; /** * @var int */ protected $lowUpperBound; /** * @var int */ protected $highLowerBound; /** * @var string */ protected $version; public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound) { $this->templatePath = $templatePath; $this->generator = $generator; $this->date = $date; $this->lowUpperBound = $lowUpperBound; $this->highLowerBound = $highLowerBound; $this->version = Version::id(); } protected function renderItemTemplate(\Text_Template $template, array $data): string { $numSeparator = ' / '; if (isset($data['numClasses']) && $data['numClasses'] > 0) { $classesLevel = $this->getColorLevel($data['testedClassesPercent']); $classesNumber = $data['numTestedClasses'] . $numSeparator . $data['numClasses']; $classesBar = $this->getCoverageBar( $data['testedClassesPercent'] ); } else { $classesLevel = ''; $classesNumber = '0' . $numSeparator . '0'; $classesBar = ''; $data['testedClassesPercentAsString'] = 'n/a'; } if ($data['numMethods'] > 0) { $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); $methodsNumber = $data['numTestedMethods'] . $numSeparator . $data['numMethods']; $methodsBar = $this->getCoverageBar( $data['testedMethodsPercent'] ); } else { $methodsLevel = ''; $methodsNumber = '0' . $numSeparator . '0'; $methodsBar = ''; $data['testedMethodsPercentAsString'] = 'n/a'; } if ($data['numExecutableLines'] > 0) { $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); $linesNumber = $data['numExecutedLines'] . $numSeparator . $data['numExecutableLines']; $linesBar = $this->getCoverageBar( $data['linesExecutedPercent'] ); } else { $linesLevel = ''; $linesNumber = '0' . $numSeparator . '0'; $linesBar = ''; $data['linesExecutedPercentAsString'] = 'n/a'; } $template->setVar( [ 'icon' => $data['icon'] ?? '', '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' => $data['testedClassesPercentAsString'] ?? '', 'classes_level' => $classesLevel, 'classes_number' => $classesNumber, ] ); return $template->render(); } protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node): void { $template->setVar( [ 'id' => $node->getId(), 'full_path' => $node->getPath(), 'path_to_root' => $this->getPathToRoot($node), 'breadcrumbs' => $this->getBreadcrumbs($node), 'date' => $this->date, 'version' => $this->version, 'runtime' => $this->getRuntimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound, ] ); } protected function getBreadcrumbs(AbstractNode $node): string { $breadcrumbs = ''; $path = $node->getPathAsArray(); $pathToRoot = []; $max = \count($path); if ($node instanceof FileNode) { $max--; } for ($i = 0; $i < $max; $i++) { $pathToRoot[] = \str_repeat('../', $i); } foreach ($path as $step) { if ($step !== $node) { $breadcrumbs .= $this->getInactiveBreadcrumb( $step, \array_pop($pathToRoot) ); } else { $breadcrumbs .= $this->getActiveBreadcrumb($step); } } return $breadcrumbs; } protected function getActiveBreadcrumb(AbstractNode $node): string { $buffer = \sprintf( ' ' . "\n", $node->getName() ); if ($node instanceof DirectoryNode) { $buffer .= ' ' . "\n"; } return $buffer; } protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string { return \sprintf( ' ' . "\n", $pathToRoot, $node->getName() ); } protected function getPathToRoot(AbstractNode $node): string { $id = $node->getId(); $depth = \substr_count($id, '/'); if ($id !== 'index' && $node instanceof DirectoryNode) { $depth++; } return \str_repeat('../', $depth); } protected function getCoverageBar(float $percent): string { $level = $this->getColorLevel($percent); $template = new \Text_Template( $this->templatePath . 'coverage_bar.html', '{{', '}}' ); $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]); return $template->render(); } protected function getColorLevel(float $percent): string { if ($percent <= $this->lowUpperBound) { return 'danger'; } if ($percent > $this->lowUpperBound && $percent < $this->highLowerBound) { return 'warning'; } return 'success'; } private function getRuntimeString(): string { $runtime = new Runtime; $buffer = \sprintf( '%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion() ); if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) { $buffer .= \sprintf( ' with Xdebug %s', \phpversion('xdebug') ); } if ($runtime->hasPCOV() && !$runtime->hasPHPDBGCodeCoverage()) { $buffer .= \sprintf( ' with PCOV %s', \phpversion('pcov') ); } return $buffer; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Html; use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; /** * Renders a directory node. */ final class Directory extends Renderer { /** * @throws \InvalidArgumentException * @throws \RuntimeException */ public function render(DirectoryNode $node, string $file): void { $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( [ 'id' => $node->getId(), 'items' => $items, ] ); $template->renderTo($file); } protected function renderItem(Node $node, bool $total = false): string { $data = [ 'numClasses' => $node->getNumClassesAndTraits(), 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), 'numMethods' => $node->getNumFunctionsAndMethods(), 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), 'linesExecutedPercent' => $node->getLineExecutedPercent(false), 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), 'numExecutedLines' => $node->getNumExecutedLines(), 'numExecutableLines' => $node->getNumExecutableLines(), 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), ]; if ($total) { $data['name'] = 'Total'; } else { if ($node instanceof DirectoryNode) { $data['name'] = \sprintf( '%s', $node->getName(), $node->getName() ); $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); $data['icon'] = \sprintf('', $up); } else { $data['name'] = \sprintf( '%s', $node->getName(), $node->getName() ); $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); $data['icon'] = \sprintf('', $up); } } return $this->renderItemTemplate( new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'), $data ); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeCoverage\Report\Html; use SebastianBergmann\CodeCoverage\Node\AbstractNode; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; /** * Renders the dashboard for a directory node. */ final class Dashboard extends Renderer { /** * @throws \InvalidArgumentException * @throws \RuntimeException */ public function render(DirectoryNode $node, string $file): void { $classes = $node->getClassesAndTraits(); $template = new \Text_Template( $this->templatePath . 'dashboard.html', '{{', '}}' ); $this->setCommonTemplateVariables($template, $node); $baseLink = $node->getId() . '/'; $complexity = $this->complexity($classes, $baseLink); $coverageDistribution = $this->coverageDistribution($classes); $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); $projectRisks = $this->projectRisks($classes, $baseLink); $template->setVar( [ 'insufficient_coverage_classes' => $insufficientCoverage['class'], 'insufficient_coverage_methods' => $insufficientCoverage['method'], 'project_risks_classes' => $projectRisks['class'], 'project_risks_methods' => $projectRisks['method'], 'complexity_class' => $complexity['class'], 'complexity_method' => $complexity['method'], 'class_coverage_distribution' => $coverageDistribution['class'], 'method_coverage_distribution' => $coverageDistribution['method'], ] ); $template->renderTo($file); } /** * Returns the data for the Class/Method Complexity charts. */ protected function complexity(array $classes, string $baseLink): array { $result = ['class' => [], 'method' => []]; foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { if ($className !== '*') { $methodName = $className . '::' . $methodName; } $result['method'][] = [ $method['coverage'], $method['ccn'], \sprintf( '%s', \str_replace($baseLink, '', $method['link']), $methodName ), ]; } $result['class'][] = [ $class['coverage'], $class['ccn'], \sprintf( '%s', \str_replace($baseLink, '', $class['link']), $className ), ]; } return [ 'class' => \json_encode($result['class']), 'method' => \json_encode($result['method']), ]; } /** * Returns the data for the Class / Method Coverage Distribution chart. */ protected function coverageDistribution(array $classes): array { $result = [ 'class' => [ '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, ], 'method' => [ '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) { foreach ($class['methods'] as $methodName => $method) { if ($method['coverage'] === 0) { $result['method']['0%']++; } elseif ($method['coverage'] === 100) { $result['method']['100%']++; } else { $key = \floor($method['coverage'] / 10) * 10; $key = $key . '-' . ($key + 10) . '%'; $result['method'][$key]++; } } if ($class['coverage'] === 0) { $result['class']['0%']++; } elseif ($class['coverage'] === 100) { $result['class']['100%']++; } else { $key = \floor($class['coverage'] / 10) * 10; $key = $key . '-' . ($key + 10) . '%'; $result['class'][$key]++; } } return [ 'class' => \json_encode(\array_values($result['class'])), 'method' => \json_encode(\array_values($result['method'])), ]; } /** * Returns the classes / methods with insufficient coverage. */ protected function insufficientCoverage(array $classes, string $baseLink): array { $leastTestedClasses = []; $leastTestedMethods = []; $result = ['class' => '', 'method' => '']; foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { if ($method['coverage'] < $this->highLowerBound) { $key = $methodName; if ($className !== '*') { $key = $className . '::' . $methodName; } $leastTestedMethods[$key] = $method['coverage']; } } if ($class['coverage'] < $this->highLowerBound) { $leastTestedClasses[$className] = $class['coverage']; } } \asort($leastTestedClasses); \asort($leastTestedMethods); foreach ($leastTestedClasses as $className => $coverage) { $result['class'] .= \sprintf( ' %s%d%%' . "\n", \str_replace($baseLink, '', $classes[$className]['link']), $className, $coverage ); } foreach ($leastTestedMethods as $methodName => $coverage) { [$class, $method] = \explode('::', $methodName); $result['method'] .= \sprintf( ' %s%d%%' . "\n", \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $coverage ); } return $result; } /** * Returns the project risks according to the CRAP index. */ protected function projectRisks(array $classes, string $baseLink): array { $classRisks = []; $methodRisks = []; $result = ['class' => '', 'method' => '']; foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) { $key = $methodName; if ($className !== '*') { $key = $className . '::' . $methodName; } $methodRisks[$key] = $method['crap']; } } if ($class['coverage'] < $this->highLowerBound && $class['ccn'] > \count($class['methods'])) { $classRisks[$className] = $class['crap']; } } \arsort($classRisks); \arsort($methodRisks); foreach ($classRisks as $className => $crap) { $result['class'] .= \sprintf( ' %s%d' . "\n", \str_replace($baseLink, '', $classes[$className]['link']), $className, $crap ); } foreach ($methodRisks as $methodName => $crap) { [$class, $method] = \explode('::', $methodName); $result['method'] .= \sprintf( ' %s%d' . "\n", \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $crap ); } return $result; } protected function getActiveBreadcrumb(AbstractNode $node): string { return \sprintf( ' ' . "\n" . ' ' . "\n", $node->getName() ); } } Code Coverage for {{full_path}}
{{items}}
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
{{lines}}
{{icon}}{{name}} {{lines_bar}}
{{lines_executed_percent}}
{{lines_number}}
{{methods_bar}}
{{methods_tested_percent}}
{{methods_number}}
{{classes_bar}}
{{classes_tested_percent}}
{{classes_number}}
{{name}} {{methods_bar}}
{{methods_tested_percent}}
{{methods_number}}
{{crap}} {{lines_bar}}
{{lines_executed_percent}}
{{lines_number}}
Dashboard for {{full_path}}

Classes

Coverage Distribution

Complexity

Insufficient Coverage

{{insufficient_coverage_classes}}
Class Coverage

Project Risks

{{project_risks_classes}}
Class CRAP

Methods

Coverage Distribution

Complexity

Insufficient Coverage

{{insufficient_coverage_methods}}
Method Coverage

Project Risks

{{project_risks_methods}}
Method CRAP
{{percent}}% covered ({{level}})
{{name}} {{classes_bar}}
{{classes_tested_percent}}
{{classes_number}}
{{methods_bar}}
{{methods_tested_percent}}
{{methods_number}}
{{crap}} {{lines_bar}}
{{lines_executed_percent}}
{{lines_number}}
.octicon { display: inline-block; vertical-align: text-top; fill: currentColor; } .nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;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}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc}body { padding-top: 10px; } .popover { max-width: none; } .octicon { margin-right:.25em; } .table-bordered>thead>tr>td { border-bottom-width: 1px; } .table tbody>tr>td, .table thead>tr>td { padding-top: 3px; padding-bottom: 3px; } .table-condensed tbody>tr>td { padding-top: 0; padding-bottom: 0; } .table .progress { margin-bottom: inherit; } .table-borderless th, .table-borderless td { border: 0 !important; } .table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { background-color: #dff0d8; } .table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { background-color: #c3e3b5; } .table tbody tr.covered-by-small-tests, li.covered-by-small-tests { background-color: #99cb84; } .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: 117px; } td.small { } td.codeLine { font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", 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; } #toplink { position: fixed; left: 5px; bottom: 5px; outline: 0; } svg text { font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #666; fill: #666; } .scrollbox { height:245px; overflow-x:hidden; overflow-y:scroll; } /*! * Bootstrap v4.3.0 (https://getbootstrap.com/) * Copyright 2011-2019 The Bootstrap Authors * Copyright 2011-2019 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} /*# sourceMappingURL=bootstrap.min.css.map */ 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-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}.

/*! * Bootstrap v4.3.0 (https://getbootstrap.com/) * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Ae="show",Ne="out",Oe={HIDE:"hide"+Ce,HIDDEN:"hidden"+Ce,SHOW:"show"+Ce,SHOWN:"shown"+Ce,INSERTED:"inserted"+Ce,CLICK:"click"+Ce,FOCUSIN:"focusin"+Ce,FOCUSOUT:"focusout"+Ce,MOUSEENTER:"mouseenter"+Ce,MOUSELEAVE:"mouseleave"+Ce},ke="fade",Pe="show",Le=".tooltip-inner",je=".arrow",He="hover",Re="focus",Ue="click",We="manual",xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Pe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(ke);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Pe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ne&&e._leave(null,e)};if(g(this.tip).hasClass(ke)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==Ae&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Pe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ue]=!1,this._activeTrigger[Re]=!1,this._activeTrigger[He]=!1,g(this.tip).hasClass(ke)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Se+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Le)),this.getTitle()),g(t).removeClass(ke+" "+Pe)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return De[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==We){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Re:He]=!0),g(e.getTipElement()).hasClass(Pe)||e._hoverState===Ae?e._hoverState=Ae:(clearTimeout(e._timeout),e._hoverState=Ae,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Ae&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Re:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ne,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ne&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,g(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(ye,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(ke),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ee),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ee,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.0"}},{key:"Default",get:function(){return we}},{key:"NAME",get:function(){return ye}},{key:"DATA_KEY",get:function(){return Ee}},{key:"Event",get:function(){return Oe}},{key:"EVENT_KEY",get:function(){return Ce}},{key:"DefaultType",get:function(){return Ie}}]),i}();g.fn[ye]=xe._jQueryInterface,g.fn[ye].Constructor=xe,g.fn[ye].noConflict=function(){return g.fn[ye]=Te,xe._jQueryInterface};var Fe="popover",qe="bs.popover",Me="."+qe,Ke=g.fn[Fe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},xe.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},xe.DefaultType,{content:"(string|element|function)"}),Xe="fade",ze="show",Ge=".popover-header",Je=".popover-body",Ze={HIDE:"hide"+Me,HIDDEN:"hidden"+Me,SHOW:"show"+Me,SHOWN:"shown"+Me,INSERTED:"inserted"+Me,CLICK:"click"+Me,FOCUSIN:"focusin"+Me,FOCUSOUT:"focusout"+Me,MOUSEENTER:"mouseenter"+Me,MOUSELEAVE:"mouseleave"+Me},$e=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Qe+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(Ge),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Je),e),t.removeClass(Xe+" "+ze)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("